大佬们点波关注呀,从小家境贫寒,没见过四位数字,让我关注人数破千吧~
1.销毁和未创建调用
及时停止或者销毁监听,例如一个定时器:
Timer _countdownTimer; @override void dispose() { _countdownTimer?.cancel(); _countdownTimer = null; super.dispose(); }
为了保险我们还要在调用setState()
前判断当前页面是否存在:
_countdownTimer = Timer.periodic(Duration(seconds: 2), (timer) { if (mounted){ setState(() { }); } });
2.先绘制再请求
addPostFrameCallback
回调方法在Widget渲染完成时触发,所以一般我们在获取页面中的 Widget 大小、位置时使用到。
解决方法就是使用 addPostFrameCallback
回调方法,等待页面 build 完成后在请求数据:
@override void initState() { WidgetsBinding.instance.addPostFrameCallback((_){ /// 接口请求 }); }
3.保持页面状态
比如点击导航栏来回切换页面,默认情况下会丢失原页面状态,也就是每次切换都会重新初始化页面。这种情况解决方法就是 PageView
与BottomNavigationBar
结合使用,同时子页面 State
中继承 AutomaticKeepAliveClientMixin
并重写 wantKeepAlive
为true。代码大致如下:
class _TestState extends State<Test> with AutomaticKeepAliveClientMixin{ @override Widget build(BuildContext context) { super.build(context); return Container(); } @override bool get wantKeepAlive => true; }
4.预先缓存图片
在 Flutter 中,加载本地图片会存在一个加载过程。比如点击图标做图标的切换时,那么首次会发生闪动的情况。尤其是做类似引导页这类需求是,通过左右滑动切换图片时会发生比较明显的白屏一闪而过。
解决方法很简单,就是使用 precacheImage,它将图像预存到图像缓存中。如果图像稍后被 Image、BoxDecation 或 FadeInImage 使用,它会被加载得更快。
precacheImage(AssetImage("assets/logo"), context);
本问题详细的代码见:点击查看。
5.屏幕方向
新建的 Flutter 项目默认并没有限制屏幕的横竖屏,所以如果你的项目并没有适配横竖屏,需要限制某一方向。我以限制竖屏为例:
void main(){ SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown ]).then((_){ runApp(MyApp()); }); }
6.FutureBuilder 懒加载
Flutter 中通过 FutureBuilder
或者 StreamBuilder
可以和简单的实现懒加载,通过 future
或者 stream
“异步” 获取数据,之后通过 AsyncSnapshot
的 data 再去加载数据,至于流和异步的概念,以后再展开吧。
const FutureBuilder({ Key key, this.future,//获取数据的方法 this.initialData, @required this.builder//根据快照的状态,返回不同的widget }) : assert(builder != null), super(key: key);
future 就是一个定义的异步操作,注意要带上泛型,不然后面拿去 snapshot.data 的时候结果是 dynamic 的 snapshot 就是 future 这个异步操作的状态快照,根据它的 connectionState 去加载不同的 widget 有四种快照的状态:
enum ConnectionState { //future还未执行的快照状态 none, //连接到一个异步操作,并且等待交互,一般在这种状态的时候,我们可以加载个菊花 waiting, //连接到一个活跃的操作,比如stream流,会不断地返回值,并还没有结束,一般也是可以加载个菊花 active, //异步操作执行结束,一般在这里可以去拿取异步操作执行的结果,并显示相应的布局 done, }
7.StreamBuilder 流控制管理
从一端发射一个事件,从另外一端去监听事件的变化,通过 Stream 我们可以在 Flutter 上设计出基于事件流驱动的响应式代码逻辑。常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。
class StreamDemo extends StatefulWidget { @override _StreamDemoState createState() => _StreamDemoState(); } class _StreamDemoState extends State<StreamDemo> { var index = 0; var streamController; StreamSink<String> get streamSink => streamController.sink; Stream<String> get streamData => streamController.stream; @override void initState() { super.initState(); streamController = StreamController<String>(); streamSink.add("0"); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('streamBuilder')), body: StreamBuilder<String>( stream: streamData, builder: (BuildContext context, AsyncSnapshot<String> snapshot) { return Text('Result: ${snapshot.data}'); }, ), floatingActionButton: FloatingActionButton( onPressed: onFloatActionButtonPress, child: Icon(Icons.add))); } void onFloatActionButtonPress() { index++; streamSink.add(index.toString()); } }
8.Future 发起多个异步操作的方式
复杂耗时操作或者多个网络请求并对完整性要求较高的操作,可以使用 Future
异步方法:
Future.wait([ // 2秒后返回结果 Future.delayed(new Duration(seconds: 2), () { return "hello"; }), // 4秒后返回结果 Future.delayed(new Duration(seconds: 4), () { return " world"; }) ]).then((results){ print(results[0]+results[1]); }).catchError((e){ print(e); });
9.Text 上下边距调整
左侧的彩色矩形是支柱(尽管实际上支柱没有宽度)。这个矩形的高度是最小的线条高度。这条线不能再短了。但它可以更高。
- 上升是从基线到文本顶部的距离(由字体定义,而不是任何特定的字形)
- 下降是从基线到文本底部的距离(由字体定义,而不是任何特定的字形)
前导(读作“ledding”,如旧排字机用来排字的铅字金属)是一行底端和下一行顶端之间的距离。在支柱中,一半的引线放在顶部,一半放在底部。是图中的灰色区域。 > 可以使用乘数更改支柱的垂直尺寸。
class TextLineHeight extends StatelessWidget { final String textContent; final double leading; final double textLineHeight; final double fontSize; TextLineHeight({ this.textContent, this.leading: 0, this.textLineHeight: 0.93, this.fontSize: 26, }); @override Widget build(BuildContext context) { return Transform.translate( offset: Offset(0, fontSize/11.52), child: Text( textContent, strutStyle: StrutStyle( forceStrutHeight: true, height: textLineHeight, leading: leading, fontSize: fontSize, ), style: TextStyle( fontSize: fontSize, color: Colors.black, ), ), ); } }
效果对比:
10.空判断
使用 null-aware operators
判断 null,减少代码量。
// User below title ??= "Title"; // instead of if (title == null) { title = "Title"; }
上面的方法可以在只有一层判断中做保护,如果你有一连串的’.’取值,那就需要用这种方法了。
xxCount = xxModel?.xxInfo?.xxCount ?? 0
11.VSCode 配置项
{ "version": "0.2.0", "configurations": [ { "name": "Main", "type": "dart", "request": "launch", "program": "lib/main.dart”, "flutterMode": "profile" # 测试完后记得把它改回去! }, { "name": "Dev", "type": "dart", "request": "launch", "program": "lib/main_dev.dart", "args": [ "--flavor", "universal", ], "flutterMode": "profile" # 测试完后记得把它改回去! }, { "name": "Prod", "type": "dart", "request": "launch", "program": "lib/main_prod.dart", "args": [ "--flavor", "universal", ], }, ] }