欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

Flutter入门系列-开发经验贴

时间:2023-07-04
1、State中 build 重复调用

在有状态的 StatefulWidget中,build 方法会重复调用两次,所有如果在build 方法中使用 FutureBuilder 这种延迟加载控件【即先获取网络或者本地数据,然后再去创建Widget的组件】,future 对象不能在构造方法中进行调用,而是应该放在 initState 方法中进行。

@override void initState() { _future = _loadDefaultData(); //这么写,避免重复调用两次 super.initState(); } Future _loadDefaultData() async { HiCache hiCache = await HiCache.preInit(); String defaultUserName = hiCache?.get(LoginDao.USER_NAME)?? ''; String defaultPassword = hiCache?.get(LoginDao.USER_PWD)??''; Map map = {"name": defaultUserName, "pwd": defaultPassword}; return map; } return Scaffold( appBar: appBar('密码登录', '注册', widget.onJumpToRegister), body: FutureBuilder( future: _future, builder: (BuildContext context, AsyncSnapshot snapshot){ if (snapshot.connectionState == ConnectionState.done){ if (snapshot.hasData){ String name; String pwd; if (isDefaultLoaded) { name = userName; pwd = password; } else { Map userData = snapshot.data; name = userData['name']; pwd = userData['pwd']; isDefaultLoaded = true; } return _loginBody(name, pwd); } else { print("没有数据....."); return _loginBody("", ""); // 没有数据..... } } else { print("加载进行中....."); return _loginBody("", ""); //加载进行中..... } })

2、判断当前App运行模式

判断当前App是否以 releaseMode 模型运行 

print("kReleaseMode $kReleaseMode");

3、自建 Flutter 系统错误处理框架

自建的Flutter 系统错误处理框架

//run(Widget app){...}//框架异常 即Flutter 框架自生所产生的Error,不是我们的代码产生的ErrorFlutterError.onError = (FlutterErrorDetails details) async { //线上环境,走上报逻辑 if (kReleaseMode){ Zone.current.handleUncaughtError(details.exception, details.stack); } else { //开发期间,走console 抛出 FlutterError.dumpErrorToConsole(details); }};

处理自己写出的错误框架

runZonedGuarded(() { runApp(app);}, (e, s){ //print('HiZone $e'); _reportError(e, s);});//通过接口上报异常信息_reportError(Object error, StackTrace s){ print("kReleaseMode $kReleaseMode"); print("object $error");//可以自己搭建一个错误处理框架}

总的代码如下: 

import 'dart:async';import 'package:flutter/cupertino.dart';import 'package:flutter/foundation.dart';import 'package:flutter/material.dart';class HiZone { run(Widget app) { //框架异常 即Flutter 框架自生所产生的Error,不是我们的代码产生的Error FlutterError.onError = (FlutterErrorDetails details) async { //线上环境,走上报逻辑 if (kReleaseMode){ Zone.current.handleUncaughtError(details.exception, details.stack); } else { //开发期间,走console 抛出 FlutterError.dumpErrorToConsole(details); } }; runZonedGuarded(() { runApp(app); }, (e, s){ //print('HiZone $e'); _reportError(e, s); }); } //通过接口上报异常信息 _reportError(Object error, StackTrace s){ print("kReleaseMode $kReleaseMode"); print("object $error"); }}

4、Flutter 接口

Flutter 中接口和抽象类类似,都是用 abstract 关键字来标明

//弹幕组件abstract class IBarrage { //发送弹幕消息 void send(String message); //暂停 void pause(); //播放弹幕消息 void play();}

5、Flutter 中 typedef 的使用

typedef 的用法有点类似Java中的接口,但是Java接口中的是例如 xxInterface.on(data),这种写法,而Flutter中是直接 xxxCallback(data) 这种形式来callback,感觉认为函数也是一等公民,可以直接作为引用直接调用,也可以直接作为参数传递。

class NetError { final int statusCode; final String message; NetError(this.statusCode, this.message);}typedef NetErrorCallback(NetError netError);class HttpRequest { void sendRequest() { Future.delayed(Duration(seconds: 1), (){ try { if (callback!=null) { callback(NetError(101, '发生未知错误')); } return 10+11; } catch (e) { throw Exception(e); } }).then((value) => print(value)).onError((error, stackTrace) => print(error)); } NetErrorCallback callback; void addNetErrorCallback(NetErrorCallback callback){ this.callback = callback; }}//使用var httpRequest = HttpRequest();httpRequest.addNetErrorCallback((netError) => print(netError.message));httpRequest.sendRequest();

6、函数类型的参数

void doFunction(int doAdd(int age), int num){ int result = doAdd(num); print("doAdd result is $result");}void main() { print("hello "); int add(int count){ return 10+count; } doFunction(add, 100); doFunction((age) => 12+age, 100);}

7、Flutter 设置图片缓存

Flutter 应该是没有磁盘缓存,只有内存缓存,所以为了提高图片加载的效率,需要设置图片缓存的大小。

//在初始化偏好设置的init方法中调用如下代码//图片缓存大小设置//image_cache.dart//const int _kDefaultSize = 1000;//const int _kDefaultSizeBytes = 100 << 20; // 100 MiBPaintingBinding.instance.imageCache.maximumSize = 2000; //默认是1000张图片 现在改成2000张PaintingBinding.instance.imageCache.maximumSizeBytes = 200 << 20; //改成200M

8、关于多层次异步任务调用

void main() { doBusiness() async { int result = await Business.sleep(); //因为sleep方法返回的是Future类型的,所以要使用await来等待并获取返回值,同时用async来标记方法 print("result is $result"); } //另外,从该例子中学习到了,只要Dart中还有异步任务[事件任务,不是微任务],那么就不会结束进程,休息了5秒之后,才回结束掉进程 doBusiness(); //这里执行的是异步任务,不会阻塞主线程,但是不同与Java中的异步任务,Java是多线程,Dart是单线程,异步是基于事件任务队列执行的 print("main线程结束...");}class Business { static sleep(){ //返回的数据类型是Future类型的 return doSomeSleep(); } static doSomeSleep() async{ //async 标记该方法是异步任务,返回的数据是Future类型的 await Future.delayed(Duration(seconds: 5)); return 100; // }}

9、Positioned 组件的使用

//positioned widget 用于在Stack控件中定位位置,left, right, bottom, top stack( children:[ Positioned( left:0, right:0, bottom:0, chiild:Container(...), ), ..、 ])

10、渐变色 LinearGradient 

//在Container中利用decoration来添加渐变色decoration: BoxDecoration ( //渐变 gradient: LinearGradient ( begin: Alignment.bottomCenter, //渐变色开始的位置 end: Alignment.topCenter,//渐变色结束的位置 colors: [Colors.black54, Colors.transparent]//分别对应开始和结束位置的颜色 ))

11、ScrollController 的使用

ScrollController scrollController = ScrollController();scrollController.addListener(() { // maxScrollExtent 最大的可滚动距离 pixels 是当前的已经滚动距离 // dis = _scrollController.position.maxScrollExtent - _scrollController.position.pixels // 当前距离页面底部还有剩余多少距离 // 怎么看 pixels 这个参数呢? // 可以这么看,就是顶部控件超过顶部这条线到距离大小是多少,然后用总到大小减去就是剩余底部的距离 var dis = scrollController.position.maxScrollExtent - scrollController.position.pixels; if ( dis < 200 && !isLoadingMore && scrollController.position.maxScrollExtent!=0 && scrollController.position.pixels>0) { print('dis $dis position.pixels ${scrollController.position.pixels}'); loadData(loadMore: true);//开始加载更多 }});

12、const widget 常量Widget 

1、利用 const 构造无状态Widget

class ChildWidget extends StatelessWidget { final String myText; const ChildWidget(this.myText, {Key key}) : super(key: key); @override Widget build(BuildContext context) { return Container( child: Text(myText), ); }}

2、在父Widget 中的 State 中使用 ChildWidget

class ParentWidget extends StatefulWidget { const ParentWidget({Key key}) : super(key: key); @override _ParentWidgetState createState() => _ParentWidgetState();}class _ParentWidgetState extends State { static const String myText = "hello world"; @override Widget build(BuildContext context) { return Scaffold( body: SafeArea(child: Column( children: [ const ChildWidget(myText), ], )), ); }}

3、const Widget 的优势

首先总结下,const 在Dart 语言中特点:

1、一个Dart 类的对象是否能用 const 修饰,取决于类的构造方法是否被 const 修饰;

2、使用const 修饰的构造方法中,所有的成员变量必须被 final 修饰;

3、构造 const 对象时,传参也必须是 const 的常量;

4、const 修饰的构造方法,不能有方法体;

对于Flutter来说,const 修饰的优化点:

利用常量池复用 Widget,在更新频繁的 Widget 场景中,有优化作用,避免了 Widget 的回收和重建;const 对GC 有一定的抑制作用,在创建大量相同对象的场景下,创建的对象少了,自然GC也会变少。

const 对GC的优化

在Flutter 中,Widget  作为配置信息,本身被设计的非常轻量,就是为了适应频繁的销毁重建,这个操作必然会引起对旧 Widget 对象的回收。

当我们使用 const 关键字声明常量的时候,背后是利用的类似常量池的概念,将 const 的对象缓存在常量池中,以待后续复用。

常量会具备普通对象更长的生命周期,这有好处也有坏处。好处就是常量对象会对 GC不那么敏感,也就是不需要频繁的触发GC。坏处是常量池中的生命周期较长,可能会导致不常用的对象被缓存后,没有适合释放时机,导致内存占用过高。

实测下来,const 确实对 GC 有一些影响。

13、Flutter 中 function 和 method 区别

 Flutter 中function 和 method 是有区别的,function 表示一个功能,可以独立使用,是一等公民,而 method 不能单独使用,必须由类的实例来进行调用。

14、强制改变屏幕方向

dependencies: orientation: ^1.2.0//使用OrientationPlugin.forceOrientation(DeviceOrientation.portraitUp);

15、属性扩展

enum RouteStatus {homePage, videoListPage, videoDetailPage, loginPage, registrationPage, unknownPage}//给枚举扩展属性extension RouteStatusExtension on RouteStatus{ String get name => ['homePage', 'videoListPage', 'videoDetailPage','loginPage','registrationPage','unknownPage'][index];}

16、创建Model类

在AndroidStudio中使用JsonToDart插件,在创建好xx_moel.dart 文件之后,在文件空白地方,右键 generate -> jsonToDart,  贴入json字符串就可以生成对应的model类。

17、操作符

// A?.B 如果A为null, 那么A?.B 返回的就是null 反之 如果A不为null, 那么A?.B的就是A.B// A??B 如果A为null, 那么A??B 返回的就是B 否则就是不做任何处理 返回的就是A

18、ViewPort

ViewPort 可以大于屏幕高度,也可以小于屏幕高度,是确定可滚动范围使用的。 

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。