# flutter_base **Repository Path**: ZhangQQ_123/flutter_base ## Basic Information - **Project Name**: flutter_base - **Description**: 搭建一个简单的flutter框架 - **Primary Language**: Dart - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-03-18 - **Last Updated**: 2022-07-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: Flutter ## README # flutter_base 提供一个flutter app的基础框架,包含网络请求,弹框,basePage等的一些封装 pages.demo文件夹下为一些示例 注意:不要再android和ios文件夹中编写native逻辑,如需原生能力,以插件的形式实现 # 项目创建: ![](https://gitee.com/ZhangQQ_123/DeveloperHelper/raw/master/pics/screenshot/%E6%88%AA%E5%B1%8F2021-03-23%20%E4%B8%8A%E5%8D%8811.35.08.png) # Dart语法: ### 1、基础语法: Dart语言以main方法为入口开始运行,没有public、private等关键字用于表示作用域声明。而是以“_”符号开头的方法或变量表示为私有变量。通过@protected注解表示调用保护。 ### 2、final/const: Dart中使用final和const表示常量,final表示运行时常量,const表示编译时常量。 ### 3、import: import关键字用于实现类导入,同时还可以用“as”增加导入的自定义别名 ### 4、基础数据类型: Dart中支持数字、字符串、布尔、数组、集合等数据类型。 在Dart2.3中引入了扩展运算符“...”和判空扩展运算符“...?” 赋值操作符可以让开发者减少一些逻辑判断代码: AA ?? "99" //表示如果AA为空,则返回999 AA??="999" //表示如果AA为空,则将999赋值给AA AA~/999 //表示AA对于999整除 ### 5、var与dynamic Dart属于强类型语言,但可以使用var声明变量,Dart会自动推导出数据类型,dynamic声明才表示动态类型,被编译后是一个Object类型,在编译期间不对任何类型进行检查,而是在运行时对类型进行检查。 ### 6、函数方法 在Dart中还可以使用方法作为参数进行传递,如下: ```dart main() { //定义方法,如果不使用void标志,默认返回null doWhat(String str) { print(str); } //使用方法作为参数 doSomeThing(doWhat); //第二种使用方式 doSomeThing_2(doWhat); } /** * 使用方法作为参数 */ doSomeThing(void doWhat(String str)) { doWhat("哈哈,果然打印了"); } /** * 还可以这样使用 */ doSomeThing_2(void Function(String t) doWhat){ doWhat("哈哈,果然打印了_2"); } ``` 还可以定义方法变量进行方法的回调,如下: ```dart //使用typedef定义方法变量,如同 object,boolean,number 这几种被称为基本类型一样,typedef 的作用是可以自定义一种数据类型,并且这种数据类型属于 函数类型。同时 typedef 可以保留函数签名信息 typedef callback = void Function(T t); main() { //使用方法变量进行回调 doCallback((res) { print(res); }); //也可以这样使用,定义一个方法变量 callback call = (res) { print(res); }; doCallback(call); //还可以这样用,定义一个方法,直接用此方法当做参数进行传递 void callBack_2(dynamic res) { print(res); } doCallback(callBack_2); } //使用重命名的方法类型作为方法的入参 doCallback(callback call) { //调用方法 call("你好"); } //上述表达式等价于,由于没有指定泛型,所以使用动态类型dynamic doCallback_2(void Function(dynamic res) call) { call("你好"); } ``` ### 7、构造方法: Dart支持多个构造方法,默认构造方法只有一个,可通过“类名.方法名()”,自定义一个构造方法。 ### 8、异步和Isolate: Dart在单线程模式中增加了isolate提供了跨进程的异步操作,因此不会共享内存,所以也不存在死锁,但是通信只能通过port端口方式。 ```dart /** * 异步探索,dart为单线程模型,所有的任务都在主线程中执行, * 单线程通过任务的挂起和调度实现了异步操作,类似于kotlin中的协程 * * async:声明一个方法为异步方法,方法内可使用await将任务挂起 * await:与async对应,使用await使任务挂起,等待耗时任务的结果 * Future:定义一个耗时任务,简单原理为:dart主线程有两个任务队列,一个优先级高,一个优先级低,Future * 会在低优先级的任务队列的末尾插入任务,优先级高的任务队列的任务执行完后,开始执行优先级低的任务队列的任务. */ typedef Callback = void Function(T t); void main() { //在主线程中调用异步任务 getRes((res) { print(res); }); print("呵呵"); } //定义异步方法 void getRes(Callback call) async { var res = await getData(); call(res); } //定义耗时任务 Future getData() { return Future.delayed(Duration(seconds: 2), () { return "哈哈"; }); } //打印结果: //呵呵 //哈哈 ``` ### 9、扩展方法 Dart2.7支持扩展方法,使用户可以像任何类型添加新功能,并具有常规方法调用的简洁性,如下: ```dart //定义String类型的扩展函数 extension on String { printStr() { print(this); } } main() { var str = "扩展函数"; //使用扩展函数打印内容 str.printStr(); } ``` # Flutter常用控件: Container:只有一个子widget,默认充满,包含了padding、margin、color、width、height和decoration等配置 Padding:只有一个子widget,只用于设置padding,常用于嵌套child,给child设置padding Center:只有一个子widget,只用于居中显示,常用于嵌套child,给child设置居中 Stack:可以有多个widget,子widget堆叠在一起 Column:可以有多个widget,垂直布局 Row:可以有多个widget,水平布局 Expanded:只用一个子widget,在Column和Row中充满,当存在多个时均分显示,可以通过flex配置显示比例 ListView:可以有多个widget的滚动列表 ![](https://gitee.com/ZhangQQ_123/DeveloperHelper/raw/master/pics/screenshot/%E6%88%AA%E5%B1%8F2021-03-12%20%E4%B8%8B%E5%8D%881.37.50.png) ![](https://gitee.com/ZhangQQ_123/DeveloperHelper/raw/master/pics/screenshot/%E6%88%AA%E5%B1%8F2021-03-12%20%E4%B8%8B%E5%8D%881.43.47.png) ### 1、无状态控件(StatelessWidget) ### 2、有状态控件(StatefulWidget) 对于有状态的widget,当数据更新时,其实还构建了新的widget,只是state实现了数据的跨帧保存与恢复。 state具备以下生命周期: 1、initState:初始化 2、didChangeDependencies:在initState之后调用,此时可获取其他State 3、dispose:销毁 ### 3、路由跳转 Flutter中页面跳转是通过Navigator实现的,路由跳转可分为:命名路由跳转和直接使用Route跳转,命名路由跳转可以通过MaterialApp的routes属性配置路由表,而直接路由跳转需要构建Route参数信息,然后通过Navigator跳转。 ### 4.状态栏颜色 设置状态栏透明达到完全沉浸的效果: ```dart setTranStatusBar(BuildContext context) { // 移除半透明状态栏 if (Theme.of(context).platform == TargetPlatform.android) { // android 平台 SystemUiOverlayStyle _style = SystemUiOverlayStyle(statusBarColor: Colors.transparent); SystemChrome.setSystemUIOverlayStyle(_style); } } ``` 设置状态栏文字颜色: ​ 方式1: ```dart //根widget AnnotatedRegion( //设置顶栏的字体为黑色 value: SystemUiOverlayStyle.dark, //子widget child: OKToast( ), ); ``` 方式2: ```dart AppBar( //通过AppBar的brightness属性设置,Brightness.light设置状态栏文字颜色为黑色 brightness: Brightness.light, ); ``` # Flutter的核心:Widget ### 1、概念 Flutter内一切皆widget,widget是不可变的,每个widget状态都代表了一帧 通俗解释就是:Flutter中所有的界面展示效果,都是通过widget作为入口的,而widget是不可变的,所以页面发生变化的时候widget是被重新构建了的,所以widget的固定状态代表了一帧静止的画面,当画面发生变化时,对应的widget会被重新构建。 所以,widget不可变这个特性,注定了widget必须必须轻量级,不可能是真正的绘制对象,想要了解widget的完整实现,就需要提到flutter中重要的三个类:Widget、Element、RenderObject。 一个完整的Widget工作流程,从Element开始,当Widget被加载时,会创建出Widget对应的Element对象,然后Element通过Widget回去状态信息(大小,位置,文本...),最终转换为RenderObject对象进行绘制。 由此可见,Widget在流程中的定位类似于配置文件,开发人员在Widget上配置各种控件信息,最终通过RenderObject实现绘制,这个过程中最先被构建出来的是Element对象,Element对象持有Widget和RenderObject对象,成为二者的桥梁,连接起整个页面的构建流程。 总结:Flutter中的一个Widget可能被多个地方使用,而Element对象一般只会创建一次,所以Element和Widget是一对多的关系,而Element和RenderObject是一对一的关系。 ### 2.控制元素Element 在Flutter的开发过程中,Element扮演者十分重要的角色,widget的加载、生命周期很更新等流程,RenderObject的创建、更新等事件,child的加载、更新等行为都是通过Element实现或执行的. StatelessWidget无状态控件实现: ```dart abstract class StatelessWidget extends Widget { //构造函数 const StatelessWidget({ Key key }) : super(key: key); //创建对应的Element @override StatelessElement createElement() => StatelessElement(this); //创建Widget,由子类实现,最终会在对应的Element中调用 @protected Widget build(BuildContext context); } ``` StatefulWidget有状态控件实现: ```dart abstract class StatefulWidget extends Widget { //构造函数 const StatefulWidget({ Key key }) : super(key: key); //创建对应的Element @override StatefulElement createElement() => StatefulElement(this); //与无状态控件不同的是有状态创建的是State @protected @factory State createState(); } ``` 无状态控件对应的Element: ```dart /// 使用[StatelessWidget]作为其配置的[元素]。 class StatelessElement extends ComponentElement { /// Creates an element that uses the given widget as its configuration. //Element持有Widget对象 StatelessElement(StatelessWidget widget) : super(widget); @override StatelessWidget get widget => super.widget as StatelessWidget; @override Widget build() => widget.build(this); @override void update(StatelessWidget newWidget) { super.update(newWidget); assert(widget == newWidget); _dirty = true; rebuild(); } } ``` 有状态控件对应的Element: ```dart /// 使用[StatefulWidget]作为其配置的[元素]。 //相对于无状态的Element,代码相当的多,截取部分源码 class StatefulElement extends ComponentElement { //构造方法,其中“:”后面为构造方法的一些初始化操作 StatefulElement(StatefulWidget widget) : _state = widget.createState(), super(widget) { //assert断言的作用是:如果表达式的求值结果不满足需要,则打断代码的执行。可以要将提示消息附加到断言,添加一个字符串作为第二个参数。 assert(_state._element == null); _state._element = this; assert(_state._widget == null); _state._widget = widget; assert(_state._debugLifecycleState == _StateLifecycle.created); } @override Widget build() => _state.build(this); /// The [State] instance associated with this location in the tree. /// 在树中与此位置相关联的[State]实例。 /// 在[State]对象和持有它们的[StatefulElement]对象之间存在一对一的关系。[State]对象是由[mount]中的[StatefulElement]创建的。 State get state => _state; State _state; //重新组装 @override void reassemble() { state.reassemble(); super.reassemble(); } @override void _firstBuild() {} @override void performRebuild() {} //更新Widget @override void update(StatefulWidget newWidget) { super.update(newWidget); assert(widget == newWidget); final StatefulWidget oldWidget = _state._widget; // 请注意,在调用didUpdateWidget之前,我们将自己标记为dirty让作者在didUpdateWidget内调用setState而不触发断言。 _dirty = true; //替换掉老的Widget,把新的Widget保存起来 _state._widget = widget as StatefulWidget; try { _debugSetAllowIgnoredCallsToMarkNeedsBuild(true); final dynamic debugCheckForReturnedFuture = _state.didUpdateWidget(oldWidget) as dynamic; assert(); } finally { _debugSetAllowIgnoredCallsToMarkNeedsBuild(false); } //重新构建 rebuild(); } @override void activate() { super.activate(); // 因为状态可能已经观察到deactivate()并因此被处理了在构建方法中分配的资源,我们须重新构建小部件,它的状态可以重新分配它的资源。 assert(_active); // otherwise markNeedsBuild is a no-op markNeedsBuild(); } @override void deactivate() { _state.deactivate(); super.deactivate(); } @override void unmount() { super.unmount(); _state.dispose(); assert(); _state._element = null; _state = null; } @override InheritedWidget dependOnInheritedElement(Element ancestor, { Object aspect }) { assert(ancestor != null); assert(() { final Type targetType = ancestor.widget.runtimeType; if (state._debugLifecycleState == _StateLifecycle.created) {} if (state._debugLifecycleState == _StateLifecycle.defunct) {} return true; }()); return super.dependOnInheritedElement(ancestor as InheritedElement, aspect: aspect); } // 它控制是否应该调用[State]。didChangeDependencies从启动[build],以避免在[State]不会被构建时调用。当小部件从树中删除时可能会发生这种情况,但这要视情况而定在仍然在树中的[InheritedWidget]上。初始化设置为false,因为[_firstBuild]进行初始化调用 on [state]。当它为真时,[build]将调用'状态。didChangeDependencies '然后设置为false。后续调用 to [didChangeDependencies]设置为true。 bool _didChangeDependencies = false; @override void didChangeDependencies() { super.didChangeDependencies(); _didChangeDependencies = true; } } ``` 以上可知:当一个widget创建的时候,会相应的创建其对应的Element,接下来看一下Flutter程序的入口runApp()方法 其注释翻译一下,如下: 填充给定的小部件并将其附加到屏幕上。 小部件在布局过程中受到约束,迫使它填充整个屏幕。如果您希望将小部件对齐到屏幕的一边(例如,顶部),考虑使用[Align]小部件。如果你想居中你的小部件,你也可以使用[Center]小部件 再次调用[runApp]将把之前的根小部件从屏幕上分离出来并将给定的小部件附加到它的位置上。比较新的小部件树与之前的小部件树相比,任何差异都应用于底层的渲染树,类似于当一个[StatefulWidget]在调用[State.setState]后重建。 必要时使用[WidgetsFlutterBinding]初始化绑定。 * [WidgetsBinding.attachRootWidget]:创建根小部件部件层次结构。 * [RenderObjectToWidgetAdapter.attachToRenderTree]:它创建根元素的层次结构。 * [WidgetsBinding.handleBeginFrame]:它确保小部件、元素和渲染树都被构建。 ```dart //runApp中调用此方法 void attachRootWidget(Widget rootWidget) { _readyToProduceFrames = true; //根据rootWidget,生成一个RootRenderObjectElement,所以在加载根widget的时候,会对应的创建根Element _renderViewElement = RenderObjectToWidgetAdapter( container: renderView, debugShortDescription: '[root]', child: rootWidget, ).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement); } ``` # FlutterActivity FlutterActivity继承了Activity,并实现了FlutterView.Provider, PluginRegistry, ViewFactory ```java public class FlutterActivity extends Activity implements FlutterView.Provider, PluginRegistry, ViewFactory //FlutterActivity中定义了一个FlutterActivityDelegate变量,相当于FlutterActivity的代表,其持有了activity对象,并实现了生命周期的回调 private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this); ``` 查看FlutterActivityDelegate: ```java //在其onCreate方法中创建了一个FlutterView并activity.setContentView(flutterView); @Override public void onCreate(Bundle savedInstanceState) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = activity.getWindow(); window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(0x40000000); window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI); } String[] args = getArgsFromIntent(activity.getIntent()); FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args); flutterView = viewFactory.createFlutterView(activity); if (flutterView == null) { FlutterNativeView nativeView = viewFactory.createFlutterNativeView(); flutterView = new FlutterView(activity, null, nativeView); flutterView.setLayoutParams(matchParent); //添加view,FlutterView继承自SurfaceView activity.setContentView(flutterView); launchView = createLaunchView(); if (launchView != null) { addLaunchView(); } } if (loadIntent(activity.getIntent())) { return; } String appBundlePath = FlutterMain.findAppBundlePath(); if (appBundlePath != null) { runBundle(appBundlePath); } } ``` ### 关于SurfaceView 摘录自:https://blog.csdn.net/u010126792/article/details/86249399 SurfaceView提供一个直接的绘图表面(Surface)嵌入到视图结构层次中。你可以控制这个Surface的格式,大小,SurfaceView负责在屏幕上正确的摆放Surface。简单说就是SurfaceView拥有自己的Surface,它与宿主窗口是分离的。 我们知道窗口中的view共享一个window,window又对应一个Surface,所以窗口中的view共享一个Surface,而SurfaceView拥有自己的Surface。SurfaceView会创建一个置于应用窗口之后的新窗口,SurfaceView相当于在Window上挖一个洞,它就是显示在这个洞里,其他的View是显示在Window上,所以View可以显示在 SurfaceView之上,也可以添加一些层在SurfaceView之上。 SurfaceView的窗口刷新的时候不需要重绘应用程序的窗口而android普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次。 对于普通的view,Android中的窗口界面包括多个View组成的View Hierachy的树形结构,只有最顶层的DecorView才对WMS可见,这个DecorView在WMS中有一个对应的WindowState,此时APP请求创建Surface时,会在SurfaceFlinger内部建立对应的Layer。而对于SurfaceView它自带一个Surface,这个Surface在WMS有自己对应的WindowState,在SurfaceFlinger中有自己对应的layer。SurfaceView从APP端看它仍然在View hierachy结构中,但在WMS和SurfaceFlinger中它与宿主窗口是分离的。因此SurfaceView的Surface的渲染可以放到单独线程去做,不会影响主线程对事件的响应。但因为这个Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换(对SurfaceView进行ScrollBy,ScrollTo操作没有效果(还有透明度,旋转),普通View进行平移操作,内部内容会移动,SurfaceView进行这些操作内部不会移动。如果对包裹它的ViewGroup进行平移旋转等操作,也无法达到我们想要的效果。同时SurfaceView不能放在类似RecyclerView或ScrollView中,一些View中的特性也无法使用。 ### GeneratedPluginRegistrant类 GeneratedPluginRegistrant类是IED自动生成的一个类,它的主要作用是注册flutter工程依赖的插件 # flutter plugin: # flutter package: # 声明式UI: