Flutter万物皆组件,dart万物皆对象
Flutter 工程目录结构 https://blog.csdn.net/u013491829/article/details/108624331
1.跨平台移动UI框架
2.与现有的代码一起工作(可以直接嵌入到原生代码中或直接把原生代码嵌入到自己的代码中运行)
3. 完全免费、开源
1.Flutter在Debug使用JIT编译,支持热重载,能够提高我们的开发效率,而Release中利用AOT直接编译成机器码,能够达到更好的性能
2.跨多种平台,减少开发成本;支持插件,可以访问原生系统的调用。
3.有自己的engine引擎,渲染引擎skia和dartVM虚拟机
4.UI一致性
1.脱离不开原生,开发人员需要具备原生(Android、iOS)基础开发能力;
2.适配问题,开发工具版本升级后,修改量大;
3.原生集成第三方SDK后,兼容性适配是个令人头痛的问题;
4.代码可读性较差,对代码质量和管理要求较高;
5.打包后,apk/ipa要大很多;
6.Flutter packages和Dart packages上第三方sdk繁杂,适配性差,不可乱用;
7.目前几乎没有第三方开发者平台开发Flutter能力的SDK,需要原生去集成;
8.Widget的类型难以选择,糟糕的UI控件API
下列业务场景下,Flutter 明显不占据优势
1.如果你的业务场景是多框架混合开发,那 Flutter 明显不占据优势;
2.如果你的场景是需要很强的文本编辑和富文本场景,那 Flutter 明显不占据优势;
3.如果你的 KPI 对内存占用特别敏感,那 Flutter 也不是特别占据优势;
4.如果你需要热更新,那 Flutter 也并不占据优势;
官网链接:Flutter | Desktop support for Flutter
要将桌面支持添加到现有 Flutter 项目,请从项目根目录在终端中运行以下命令:
flutter create --platforms=windows,macos,linux .
这会将必要的桌面文件和目录添加到您现有的 Flutter 项目中。要仅添加特定桌面平台,请将platforms
列表更改为仅包含您要添加的平台
1.编辑DebugProfile.entitlements文件
com.apple.security.app-sandbox
com.apple.security.cs.allow-jit
com.apple.security.network.server
com.apple.security.network.client
2.编辑release.entitlements文件
com.apple.security.app-sandbox
com.apple.security.network.server
com.apple.security.network.client
Widget
下面有五个子类, PreferredSizeWidget
下面总共有6个组件, ProxyWidget
下面总共有47个组件, RenderObjectWidget
下面总共有94个组件, StateFulWidget
下面总共有167个组件, StatelessWidget
下面总共有108个组件,加上自身5个组件,所以总共的组件有高达427之多,这可能也是很多人觉得Flutter很难学的原因之一,其实我们只要掌握一些基本常用的可以了。参考:Flutter深入浅出组件篇---继承关系图 | Jimi
AppBar
和 TabBar
, 通过继承该类可实现自定义大小。Widget
的抽象 Widget
RenderObjectWidgets
为 RenderObjectElements
提供配置,它包装 RenderObjects
,提供应用程序的实际渲染。Widget
Widget
图片
显示本地图片时,要在pubspec.yaml文件里面添加如:(注意空格)
assets:
- assets/images/logo.png
图片。 https://api.flutter.dev/flutter/widgets/Image-class.html
参考 https://blog.csdn.net/du591310450/article/details/89680491
Image.asset, 本地图片 images文件夹 2.0x和3.0x Image.network 远程图片
Fit属性:BoxFit.cover最常用 显示可能拉伸,可能裁切,充满(图片要 不要求全图显示,充满整个容器,还不变形)。
BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。(一般背景图时用)
BoxFit.contain:全图显示,显示原比例,可能会有空隙。
BoxFit.scaleDown:效果和 contain 差不多,但是此属性不允许显示超过源图片大小,可小不可大(自己总结:一般显示本地小图标时使用)
BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸, 可能裁切。
BoxFit.fitHeight :高度充满(竖向充满),显示可能拉 伸,可能裁切。
圆形图片的实现: ClipOval( child:Image.network("https://www.itying.com/images/201905/ thumb_img/1101_thumb_G_1557845381862.jpg", width: 150.0, height: 150.0, )
按钮
RaisedButton---被elevatedbutton替代
OutlineButton---被OutlinedButton替代
FlatButton---被textbutton替代
文字
Text。 overflow:(设置溢出显示方式)。 Maxlines(设置行数)
输入框 textfield。 Autofocus:false,(最好设置成false)
参考:https://blog.csdn.net/zl18603543572/article/details/103772941
Flutter TextField内容垂直居中
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(borderSide: BorderSide.none),
)
点击事件
在flutter 开发中用InkWell或者GestureDetector将某个组件包起来,可添加点击事件。
GestureDetector 使用点击无水波纹出现,InkWell可以实现水波纹效果。
inkwell去除水波纹效果添加属性
splashFactory: NoSplash.splashFactory,
InkWell 水波纹会超出 Container 的圆角,把 InkWell 与 Container 设置同样的圆角.
InkWell(
onTap: (){
print("点击事件");
},
borderRadius: BorderRadius.circular(10),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10)
),
),
)
日期。 flutter_cupertino_date_picker.第三方日期库 date_format日期格式化三方库. flutter自带日期选择showdatepicker。 自带日期选择showtimepicker。
对话框。 showmodalbottomsheet.底部弹出对话框。 alertdialog普通对话框。 simpledialog选择对话框
卡片
card。 clipBehavior: //对Widget截取的行为,比如这里 Clip.antiAlias 指抗锯齿
=========================滚动组件start======================================
给滚动组件添加滚动条:Scrollbar
是一个Material风格的滚动指示器(滚动条),如果要给可滚动组件添加滚动条,只需将Scrollbar
作为可滚动组件的任意一个父级组件即可
SingleChildScrollView:类似于Android中的ScrollView
,它只能接收一个子组件,没有“懒加载”模式,所以只在内容不会超过屏幕太多时使用,且内容没超过屏幕时不滑动
ListView:它可以沿一个方向线性排布所有子组件,并且它也支持基于Sliver的延迟构建模型(懒加载),listview()构造方法没有懒加载模式,listview.builder()构造函数有懒加载模式
使用属性:cacheextent设置缓存的条目个数
Listview 横向要设置宽高。 纵向的设置高
跳转到指定的位置。 添加控制器。var scrollController=new ScrollController(); scrollController.jumpTo(0.0);
移除 ListView,GridView 的间距,MediaQuery.removeViewPadding().
MediaQuery.removeViewPadding(
context: context,
child: ListView(),
removeTop: true,
removeBottom: true,
removeLeft: true,
removeRight: true,
)
优化点:itemextent固定主轴大小,能提高性能
Dismissible组件可通过左滑或者右滑清除列表项
GridView:主要关心SliverGridDelegate,他有两个字类
SliverGridDelegateWithFixedCrossAxisCount
该子类实现了一个横轴为固定数量子元素的layout算法SliverGridDelegateWithMaxCrossAxisExtent
该子类实现了一个横轴子元素为固定最大长度的layout算法CustomScrollView:子组件必须都是sliver,CustomScrollView是使用Sliver组件创建自定义滚动效果的滚动组件,多个widget相互嵌套场景,吸顶效果场景
NestedScrollView:可以在其内部嵌套其他滚动视图的滚动视图,其滚动位置是固有链接的
系统提供的ScrollPhysics有:
ListWheelScrollView:它的渲染效果类似于车轮(或者滚筒)
ReorderableListView:是通过长按拖动某一项到另一个位置来重新排序的列表组件。
=========================滚动组件end======================================
=========================sliver系列start=====================================
控件:
1.SliverPersistentHeader:可以固定在顶部
2.SliverList。
3.SliverFixedExtentList是sliver系列组件之一,和SliverList用法一样,唯一的区别就是SliverFixedExtentList是固定子控件的高度的,SliverFixedExtentList比SliverList更加高效,因为SliverFixedExtentList无需计算子控件的布局
4.SliverFillRemaining是sliver系列组件之一,此组件充满视口剩余空间,通常用于最后一个sliver组件,以便于没有任何剩余控件。
5.SliverPrototypeExtentList和SliverList用法一样,区别是SliverPrototypeExtentList的高度由prototypeItem控件决定。
SliverPrototypeExtentList 比SliverList更加高效,因为SliverFixedExtentList无需计算子控件的布局。
SliverPrototypeExtentList比SliverFixedExtentList更加灵活,因为SliverPrototypeExtentList不必指定像素高度。
SliverPrototypeExtentList通常用于不确定item高度,随prototypeItem变化的场景,比如调整整个App字体的大小,字体越大,需要的高度越高,如果使用SliverFixedExtentList指定具体的高度,会出现字体显示不全的状况。
6.SliverFillViewport生成的每一个item都占满全屏,viewportFraction表示比率,默认是1,表示占满全屏,如果设置0.8,则在开始和结尾处出现空白
属性:
floating 设置为true时,向下滑动时,即使当前CustomScrollView不在顶部,SliverAppBar也会跟着一起向下出现
pinned 设置为true时,当SliverAppBar内容滑出屏幕时,将始终渲染一个固定在顶部的收起状态
=========================sliver系列end=====================================
深入理解 Flutter 布局约束 | Flutter 中文文档 | Flutter 中文开发者网站
Stack:帧布局 ,Positioned用于定位Stack子组件,Positioned必须是Stack的子组件,只能用于stack中
Column:线性布局,垂直方向
Row:线性布局,水平方向 mainAxisAlignment(主轴的排序方式): MainAxisAlignment.spaceEvenly,(平分比较常用) crossAxisAlignment(次轴的排序方式,这里是纵轴,用的比较少)
Expanded、Flexible和Spacer都是具有权重属性的组件,可以控制Row、Column、Flex的子控件如何布局的控件。
Row
和 UnconstrainedBox
一样, 不会对其子代施加任何约束,而是让它们成为所需的任意大小。
Row
要么使用子级的宽度,要么使用Expanded
和 Flexible
从而忽略子级的宽度
SizeBox(height:10)SizeBox(width:10)或可以使两个布局中间加10的间距
Divider()添加分割线
=========================尺寸限制类容器start================================
尺寸限制类容器用于限制容器大小,Flutter中尺寸限制类容器组件包括ConstrainedBox、UnconstrainedBox、SizedBox、AspectRatio、FractionallySizedBox、LimitedBox、Container
ConstrainedBox
有多级嵌套时,多级BoxConstraints嵌套约束最大值最终值等于多个BoxConstraints约束中的最小值,同理嵌套约束最小值等于多个BoxConstraints约束中的最大值;适用于需要设置最大/小宽高,组件大小以来子组件大小,但不能超过设置的界限。
UnconstrainedBox虽然不限制其子控件的大小,但仍然受父控件的约束,子控件的大小超出父控件的区域将会溢出。
OverflowBox
与 UnconstrainedBox
类似,但不同的是,如果其子级超出该空间,它将不会显示任何警告。
SizedBox是具有固定宽高的组件,直接指定具体的宽高;适用于固定宽高的情况,常用于当作2个组件之间间隙组件。
AspectRatio组件是固定宽高比的组件,如果组件的宽度固定,希望高是宽的1/2,可以用AspectRatio实现此效果;适用于固定宽高比的情况。
FractionallySizedBox当我们需要一个控件的尺寸是相对尺寸时,比如当前按钮的宽度占父组件的70%,可以使用FractionallySizedBox来实现此效果;适用于占父组件百分比的情况。
LimitedBox:它受到父组件的约束,此时LimitedBox将会不做任何操作,我们可以认为没有这个组件;如:父组件宽度100,它的宽度50,最终显示的效果宽度是100;适用于没有父组件约束的情况。它限制仅在获得无限约束时才适用
FittedBox
:只能在有限制的宽高中对子 widget 进行缩放(宽度和高度不会变得无限大)。否则,它将无法渲染任何内容,并且你会在控制台中看到错误。深入理解 Flutter 布局约束 | Flutter 中文文档 | Flutter 中文开发者网站
Container:适用于不仅有尺寸的约束,还有装饰(颜色、边框、等)、内外边距等需求的情况Flutter Widgets 之 Container_老孟Flutter-CSDN博客
装饰器
BoxDecoration({
Color color, //颜色
DecorationImage image,//图片
BoxBorder border, //边框
BorderRadiusGeometry borderRadius, //圆角
List boxShadow, //阴影,可以指定多个
Gradient gradient, //渐变
BlendMode backgroundBlendMode, //背景混合模式
BoxShape shape = BoxShape.rectangle, //形状
})
========================尺寸限制类容器end=================================
align
center
padding组件处理容器与子元素直接的间距
PageView控件可以实现一个“图片轮播”的效果,PageView不仅可以水平滑动也可以垂直滑动
Wrap组件,可以实现流式布局, direction,主轴的方向,默认水平。 Alignment:主轴的对齐方式 spacing:主轴方向上的间距。 Runspacing:子轴的间距。
=======================显示隐藏start========================================
Offstage控制是否显示组件. 当offstage为true,控件隐藏;类似于Android中View的gone
Offstage({
Key key,
this.offstage = true,
Widget child
})
Visibility控制子组件隐藏/可见的组件
Visibility({
Key key,
@required this.child,
this.replacement = const SizedBox.shrink(),//不可见时显示的组件(当maintainState=false)
this.visible = true,//子组件是否可见,默认true(可见)
this.maintainState = false,//不可见时是否维持状态,默认为false
this.maintainAnimation = false,//不可见时,是否维持子组件中的动画
this.maintainSize = false,//不可见时是否留有空间
this.maintainSemantics = false,//不可见时是否维持它的语义
this.maintainInteractivity = false,//不可见时是否具有交互性
})
Offstage与Visibility比较:
Offstage是控制组件隐藏/可见的组件,当Offstage不可见的时候,如果child有动画等,需要手动停掉,Offstage并不会停掉动画等操作,如果感觉有些单调功能不全,我们可以使用Visibility,
Visibility也是控制子组件隐藏/可见的组件。不同是的Visibility有隐藏状态是否留有空间、隐藏状态下是否可调用等功能。
Opacity移除控件同时它的位置依然保留,类似于Android中View的invisible
========================显示隐藏end========================================
普通路由:界面之间的跳转 Navigator.of(context)
.push(MaterialPageRoute(builder: (BuildContext context) {
return SecondPage("第二页");
}));
退出: Navigator.pop(context);
命名路由单独抽离到一个文件
final Map routes = { '/':(context,{arguments})=>Tabs(), '/search':(context,{arguments}) =>SearchPage(arguments: arguments), '/form': (context,{arguments}) =>FormPage(arguments: arguments), };
var onGenerateRoute=(RouteSettings settings) { // 统一处理 final String name = settings.name; final Function pageContentBuilder = routes[name]; if (pageContentBuilder != null) { final Route route = MaterialPageRoute( builder: (context) => pageContentBuilder(context, arguments: settings.arguments)); return route; } };
然后在根页面:MaterialApp( // home:Tabs(), initialRoute: '/', onGenerateRoute: onGenerateRoute );
替换路由
Navigator.of(context).pushReplacementNamed('/registerSecond');
返回到根路由
Navigator.of(context).pushAndRemoveUntil( new MaterialPageRoute(builder: (context) => new Tabs(index:1)), (route) => route == null );
异步函数
Dart类库有非常多的返回Future
或者Stream
对象的函数。 这些函数被称为异步函数
Stream
也是用于接收异步事件数据,和Future
不同的是,它可以接收多个异步操作的结果,它常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。实现局部刷新:1.通过抽取widget为StateFulWidget包裹,使用setState刷新制定widget2.使用StreamBuilder实现局部刷新参考:告别setState()! 优雅的UI与Model绑定 Flutter DataBus使用~ - 掘金3.Provide的解决方案设定顶级Widget,然后用consumer包裹子控件,调用更新,provider可以实现跨组件访问,(跨组件访问也可以通过context向上查询,这种方式没试过)
setState(() {});1.使用:把需要刷新的字段代码写到大括号里面(注:里面和外面都能刷新,写到里面的目的是为了判断字段代码多的情况下,辨别哪些字段需要刷新)
2.内部原理:执行了方法: _element!.markNeedsBuild();
可以通过下面方式实现刷新:(官方不推荐使用,查看BuildContext源码注释)
(context as Element).markNeedsBuild();
BuildContext 只是抽象类接口,而 Element
实现了它,向上查找
使用:获取页面宽高大小: print(((context as Element).findRenderObject() as RenderBox).size);
abstract class BuildContext {
///查找父节点中的T类型的State
T findAncestorStateOfType();
///查找父节点中的T类型的 InheritedWidget 例如 MediaQuery 等
T dependOnInheritedWidgetOfExactType({ Object aspect })
///遍历子元素的element对象
void visitChildElements(ElementVisitor visitor);
......
}
flutter中的图片:Flutter
会打把assets
下的文件打包到Android
中的assets
中,具体为app/assets/flutter_assets/
RestorationMixin 说明:Flutter 1.22 正式发布 - 知乎
Android studio 右侧的Flutter Performance打开方法:
在profile模式下不能用,在debug模式下可以
Flutter应用如何调试--DevTools介绍(下)参考链接 https://www.jianshu.com/p/0e53a168b367
Flutter 的生命周期https://blog.csdn.net/sinat_17775997/article/details/94733411
去掉右边debug图标:在根页面添加. debugShowCheckedModeBanner: false,
宽度自适应 Width:double.infinity
屏幕宽度MediaQuery.of(context).size.width,
方法的使用 加括号:调用方法。 不加括号:把方法赋值给这个属性
比如: void _ab(){} 调用方法: child:_ab(), 方法赋值: child:_ab,
通过Listener
直接识别原始指针事件来解决冲突
=======================键盘相关start=======================================
解决弹出键盘时多条线的问题(或超过屏幕):在最外层包裹一层SingleChildScrollView(),里面有list view时会有滑动冲突
/// 关闭输入法,避免弹出FocusManager.instance.primaryFocus?.unfocus();
防止空指针异常 : List travelItems; travelItems?.length ?? 0
// 防止键盘弹出,提交按钮升起。。。 resizeToAvoidBottomInset: false,
隐藏软件盘
// 隐藏键盘
// 建议传入对应的 context,才能获取当前页面的键盘是否被拉起
hiddenKeyBoard({BuildContext context}) {
Future.delayed(Duration.zero).then((value) {
FocusScopeNode _node = FocusScope.of(context );
print('_node.hasFocus: ${_node.hasFocus}');
// 根据键盘是否被拉起,来决定是否收起键盘
if (_node.hasFocus) _node.requestFocus(FocusNode());
});
}
=======================键盘相关end=======================================
接口调试可以创建假数据(动态)。 Easymock. https://easy-mock.com/
mounted 是 bool 类型,表示当前 State 是否加载到树⾥。常用于判断页面是否释放。
异步数据更新UI:
StreamBuilder是用于配合Stream
来展示流上事件(数据)变化的UI组件,凡是UI会依赖多个异步数据而发生变化的场景都可以使用StreamBuilder
FutureBuilder展示异步任务状态,会依赖一个Future
,它会根据所依赖的Future
的状态来动态构建自身
==================Dart中async和async*的区别start============================
async和await
这两个关键字的使用只需要记住两点:
只有async方法才能使用await关键字调用方法
如果调用别的async方法必须使用await关键字
async是让方法变成异步。
await是等待异步方法执行完成
简单回答这个问题就是:
•async
返回Future
.
•async*
返回Stream
.
1.async
这是异步调用。当一个函数被标记成async
的时候,意味这个方法可能要从事耗时工作,比如说网络请求、处理图片等等。被async
标记的方法会把返回值用Future
包裹一下。
Future doSomeLongTask() async {
await Future.delayed(const Duration(seconds: 1));
return 42;
}
我们可以通过await
来获取Future
里的返回值:
main() async {
int result = await doSomeLongTask();
print(result); // 等待一分钟后打印 '42'
}
2.async*
async*
比async
多了一个*
,加上*
其实是函数生成器
的意思。被async*
标记的函数会在返回一组返回值,这些返回值会被包裹在Stream
中。async*
其实是为yield
关键字发出的值提供了一个语法糖。
Stream countForOneMinute() async* {
for (int i = 1; i <= 60; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
上面的其实就是异步生成器
了。我们可以使用yield
替代return
返回数据,因为这个是时候我们的函数还在执行中。此时,我们就可以使用await for
去等待Stream
发出的每一个值了。
main() async {
await for (int i in countForOneMinute()) {
print(i); // 打印 1 到 60,一个秒一个整数
}
}
getMaoTai() async{
for (int i = 0; i <10; i++){
await Future.delayed(Duration(seconds: 1), ()async {
MaoTaiData data = await fetchMaoTaiData();
setState(){
//更新UI
};
});
}
上面的代码里使用了循环,然后每一秒钟请求依次接口,返回数据后调用setState()
更新UI。这样做会导致你每隔一两秒就setState()
一次,如果不怕性能问题,不怕产品经理打你,你这么玩玩。这个时候async*
就应该上场了:
Stream getData() async* {
for (int i = 0; i <10; i++) {
await Future.delayed(Duration(seconds: 1));
yield await fetchMaoTaiData();
}
}
这样我们就可以使用StreamBuilder
包裹下Widget
,就不必每次都去setState()
了。
==================Dart中async和async*的区别end==============================
=============================保持页面状态start==============================
保持页面状态,如何让页面保持原来的状态,而不是每次都要重新加载刷新数据呢两种方式:
第一种方式:采用IndexedStack
IndexdStack和Stack一样,都是层布局控件,可以在一个控件上面放置另一个控件,但唯一不同的是,IndexdStack在同一时刻只能显示子控件中的一个控件,通过index属性来设置显示的控件
优缺点:使用IndexedStack来保持页面状态的优点就是配置简单,但是它也有很大的缺点:IndexedStack中管理的子页面在一开始就全部一次性加载出来了,不管有没有显示出来,然后通过index属性来确定到底显示哪一个页面
代码如下:
body: IndexedStack(
index: currentIndex,
children: tabBodies
),
第二种方式:AutomaticKeepAliveClientMixin
如果所有的页面都需要保持页面状态,那么就使用indexdStack;如果有些页面需要保持页面状态,有些页面需要进来就刷新,那么我们就需要使用AutomaticKeepAliveMixin这个类来单独控制某个页面的状态
代码如下:
1. with AutomaticKeepAliveClientMixin
2.@override
bool get wantKeepAlive =>true;
===========================保持页面状态end==============================
===================flutter改成如何更改包名和应用名start==========================
android下修改应用名称:修改AndroidManifest.xml文件中的label属性。
android下修改包名:修改app下的build.gradle
defaultConfig {
applicationId "your.package.name"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
ios下修改包名和应用名称
CFBundleIdentifier
com.your.packagename
CFBundleName
app name
===================flutter改成如何更改包名和应用名start==========================
通过addPostFrameCallback可以做一些安全的操作,在有些时候是很有用的,它会在当前Frame绘制完后进行回调,并只会回调一次,如果要再次监听需要再设置
WidgetsBinding.instance.addPostFrameCallback((callback) {
_showHint();
});
系统的AppBar中的控件宽高自己设置不起作用
状态栏相关的
1.状态栏字体颜色修改
方法一:AppBar里面的Brightness属性
方法二:AnnotatedRegion
2.可以用这种方式设置渐变的AppBar
flexibleSpace: GradualChangeView(
rotation: Rotation.LR,
colors: [Colors.cyan, Colors.blue, Colors.blueAccent]),
3.沉浸式状态栏SafeArea
4.布局去掉沉浸式效果和布局设置占满全屏却无效的问题
使用Scaffold的body的布局默认是沉浸式的,将状态栏一起包含了,可以通过在body后添加一层SafeArea即可.
在Dart中实现并发可以用Isolate,它是类似于线程(thread)但不共享内存的独立运行的worker,是一个独立的Dart程序执行环境。其实默认环境就是一个main isolate。
isolate线程管理使用:在大批量调用渲染和网络请求等“高消耗”的操作下,Flutter Ui视图会造成卡顿现象,这时候要开启一个线程去跑这些操作。在使用isolate过程中注意使用完后关闭isolate并释放掉内存,否则会因内存占用大而导致应用奔溃。
基本使用:
library flutter_flutterisolate2;
import 'dart:async';
import 'dart:isolate';
main() async{
//1.创建一个和isoLate环境交流的Port
var receivePort = new ReceivePort();
//2.创建一个隔离isolate并且提供用于回执的sendPort,receivePort.sendPort是一个给当前receive发消息的sendPort
await Isolate.spawn(speak, receivePort.sendPort);
//5.现在需要一个sendPort给isolate发送消息
SendPort sendPort = await receivePort.first;
//6. 利用sendPort给isoLate发送一个消息
var resultFromIsoLate = await sendMessage2IsoLate(sendPort, 'apple');
//8.打印来自于isolate的执行结果
print(resultFromIsoLate);
resultFromIsoLate = await sendMessage2IsoLate(sendPort, 'banana');
print(resultFromIsoLate);
}
//3.创建用于在新isolate执行的函数speak
speak(SendPort sendPort) async{
//4.现在提供给主isolate一个用于给子isolate发消息的sendPort
var receivePort = new ReceivePort();
sendPort.send(receivePort.sendPort);
//单次读取
// var msgFromMainIsoLate = await receivePort.first;
// var msg = msgFromMainIsoLate[0];
// SendPort replyTo = msgFromMainIsoLate[1];
// replyTo.send("i like eat "+ msg);
//7. 读取receivePort并且回传消息, receivePort看起来是一个可迭代器
await for (var r in receivePort){
var msg = r[0];
SendPort replyTo = r[1];
replyTo.send("i like eat "+ msg);
}
}
//如果需要关闭,使用receivePort.close
//发送一条消息给isolate
Future sendMessage2IsoLate(SendPort sendPort, String msg) {
ReceivePort receivePort = ReceivePort();
sendPort.send([msg, receivePort.sendPort]);
return receivePort.first;
}
Flutter 中一般 json 数据从 String
转为 Object
的过程中都需要先经过 Map
类型。
map转json。 Map userInfo = {"username":"zhangsan","age":20}; Var a = json.encode(userInfo);
json转map. String userInfo = '{"username":"zhangsan","age":20}'; Map u = json.decode(userInfo);
json转数组
String info = "[\"嗯嗯嗯\",\"让我\",\"我无法答复\",\"问问\",\"我让人防\"]";
List
=========================provider相关start==================================
provider学习参考:(OpenFlutter公众号)Flutter状态管理:Provider4 入门教程(一)
Flutter Provider状态管理---介绍、类图分析、基本使用 | Jimi(系列文章和视频)
Flutter 主流状态管理框架 provider get 分析与思考 - 掘金
provider销毁自己手动处理参考:flutter_deer_master开源项目中GoodsSortBottomSheet类里面的处理方式
优化方式一:尽可能调整Consumer的位置
我们在上面的代码中发现Center
以及Column
组件也被Consumer
包裹了进来,但是这两个组件是不需要更新状态的,而我们每次构建的Widget
的时候,会重建整个body
,所以我们优化一下代码结构,看起来就像下面这样:
Center(child: Consumer(
builder: (_, CountNotifier1 countNotifier1, child) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
countNotifier1.count.toString(),
style: TextStyle(color: Colors.red, fontSize: 50),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: () {
countNotifier1.increment();
},
child: Text("点击加1"),
),
),
Container(
child: Column(
children: [
Text("更多组件1"),
Text("更多组件2"),
Text("更多组件3"),
Text("更多组件4"),
Text("更多组件5"),
Text("更多组件6"),
],
),
)
],
),
);
},
))
优化方式二:不需要刷新但被Consumer包裹的组件用child
比如上面我们有更多组件1-6甚至数百个组件无需刷新状态,但由于你用Consumer
包裹会导致全部刷新,那么明显会导致性能的下降,你可能会想到单独用多个Consumer
包裹需要刷新的组件就解决了,但这不就是本末倒置了吗,本身Provider
是解决代码的健壮、重复的代码,所以这个时候我们可以采用Consumer
为我们提供的child
参数,如下:
Center(
child: Consumer(
builder: (_, CountNotifier1 countNotifier1, child) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
countNotifier1.count.toString(),
style: TextStyle(color: Colors.red, fontSize: 50),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: ElevatedButton(
onPressed: () {
countNotifier1.increment();
},
child: Text("点击加1"),
),
),
child!
],
),
);
},
child: Container(
child: Column(
children: [
Text("更多组件1"),
Text("更多组件2"),
Text("更多组件3"),
Text("更多组件4"),
Text("更多组件5"),
Text("更多组件6"),
],
),
),
)
),
Selector
类和Consumer
类似,只是对build
调用Widget
方法时提供更精细的控制,简单点来说,Selector
也是一个消费者,它允许你可以从模型中准备定义哪些属性。
我们来举个例子:
比如,用户模型中有50个属性,但是我只需要更新年龄,这样我希望不需要重建用户名、电话号码等组件,那么Selector
就是用于解决这个问题,我们看一下示例:
import 'package:flutter/material.dart';
class UserModel6 with ChangeNotifier {
String name = "Jimi";
int age = 18;
String phone = "18888888888";
void increaseAge() {
age++;
notifyListeners();
}
}
return ChangeNotifierProvider(
create: (_) => UserModel6(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: SelectorExample(),
),
);
import 'package:flutter/material.dart';
import 'package:flutter_provider_example/selector_example/user_model6.dart';
import 'package:provider/provider.dart';
class SelectorExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SelectorExample"),
),
body: Center(
child: Selector(
selector: (_, userModel6) => userModel6.age,
builder: (_, age, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(age.toString(),
style: TextStyle(
color: Colors.red,
fontSize: 30
)
),
child!
],
);
},
child: Padding(
padding: EdgeInsets.all(20),
child: ElevatedButton(
onPressed: (){
Provider.of(context, listen: false).increaseAge();
},
child: Text("改变年龄"),
),
),
),
),
);
}
}
=========================provider相关end==================================
flutter_pickers:GitHub - longer96/flutter_pickers: flutter 选择器库,包括日期及时间选择器(可设置范围)、单项选择器(可用于性别、民族、学历、星座、年龄、身高、体重、温度等)、城市地址选择器(分省级、地级及县级)、多项选择器等…… 欢迎Fork & pr贡献您的代码,大家共同学习
Flutter插件,用于发现Android和iOS上的网络(WiFi和移动/蜂窝)连接状态。connectivity
Flutter常用工具类库依赖于Dart常用工具类库common_utils,以及对其他第三方库封装,致力于为大家分享简单易用工具类。如果你有好的工具类欢迎PR.目前包含SharedPreferences Util, Screen Util, Directory Util, Widget Util, Image Util。 flustars
Dart常用工具类库。包含日期,正则,倒计时,时间轴等工具类. common_utils
解决滑动冲突:外层嵌套ScrollNotificationInterceptor(没验证过)
flutter upgrade 更新flutter版本
flutter run 运行项目
flutter clean 清空
flutter --version 查看flutter版本信息
查看更详细的错误 flutter build apk --release -vv
新创建flutter项目如果想指定编程语言,比如iOS编程语言为Objective-C,Android的编程语言为Java
flutter create -i objc -a java 项目名
q退出
r或R重新加载
clean项目:1.在android目录下用命令行 gradlew clean 2.在最外层目录下用命令行 flutter clean
参考:Flutter —— Pub命令 - Belinda_sl - 博客园
状态管理
页面跳转(路由)
网络请求
保持页面状态
刷新
自定义对话框
字符串的定义方式三个双引号声明:大段落的字符串可以换行
变量:可以用Object、var与dynamic声明的变量赋任何类型的值
Object声明的变量可以是任意类型
var:声明的变量在赋值的那一刻,决定了它是什么类型,是编译期的“语法糖”。
dynamic:被编译后,实际是一个 object
类型,在编译期间不进行任何的类型检查,而是在运行期进行类型检查,是运行期的“语法糖”。
常量:final。const.
final是运行时常量,
const是编译器常量,它的值在编译期就可以确定,编译时常量能够让代码运行的更高效。
注:类的变量可以为 `final` 但是不能是 `const` 。如果 const 变量在类中,需要定义为`static const`静态常量
例子//正确,已经确定的值
const a = 1;
const b = a + 1;
//错误,final不能在编译时确定值,因此const也不能确定值
final a = 1;
const c = a + 1;
2、Dart
中 if
等语句只支持 bool
类型,switch
支持 String 类型。
3、Dart
中 number 类型分为 int 和 double ,没有 float 类型。
4、运算符
print('=====取余操作${5 % 2}=====取整操作${5 ~/ 2}');
//打印结果:=====取余操作1=====取整操作2
#################(...)和(...?)使用start###################
Dart 2.3引入了散布运算符(...)和可识别null的散布运算符(...?),它们提供了一种插入多个简明格式的简洁方法元素添加到集合中.
例如,您可以使用传播运算符(...)将列表的所有元素插入另一个列表:
var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
如果扩展运算符右边的表达式可能为null,则可以使用可识别null的扩展运算符(...?)来避免出现异常:
var list;
var list2 = [0, ...?list];
assert(list2.length == 1);
#################(...)和(...?)使用end###################
### 类型判定操作符
| `as` | 类型转换 |
| `is` | 如果对象是指定的类型返回 True |
| `is!` | 如果对象是指定的类型返回 False |
###赋值操作符
`=`、`+=`、`\=`、`*=`这些不必多说,还有一个 `??=` 操作符用来指定 值为 null 的变量的值
例子:
AA ?? "999" ///表示如果 AA 为空,返回999
AA ??= "999" ///表示如果 AA 为空,给 AA 设置成 999
AA ~/999 ///AA 对于 999 整除
### 条件表达式
Dart 有两个特殊的操作符可以用来替代 [if-else](http://dart.goodev.org/guides/language/language-tour#if-and-else) 语句:
- `condition ? expr1 : expr2`
如果 *condition* 是 true,执行 *expr1* (并返回执行的结果); 否则执行 *expr2* 并返回其结果。
- `expr1 ?? expr2`
如果 *expr1* 不为null,返回其值; 否则执行 *expr2* 并返回其结果。
?.和??的组合使用 String content = response.data?.toString() ?? '';
###级联操作符:级联操作符 (`..`) 可以在同一个对象上 连续调用多个函数以及访问成员变量。
###安全操作符:Dart提供了 `?.`操作符。左边的操作对象 如果 为 null 则返回 null
Dart
没有关键词 public
、private
等修饰符,_
下横向直接代表 private
,但是有 @protected
注解 。
6、getter setter 重写
Dart
中所有的基础类型、类等都继承 Object
,默认值是 NULL
, 自带 getter
和 setter
,而如果是 final
或者 const
的话,那么它只有一个 getter
方法,Object
都支持 getter、setter 重写:
@override
Size get preferredSize {
return Size.fromHeight(kTabHeight + indicatorWeight);
}
7、Assert(断言)
assert
只在检查模式有效,在开发过程中,assert(unicorn == null);
只有条件为真才正常,否则直接抛出异常,一般用在开发过程中,某些地方不应该出现什么状态的判断。
8、关键字
mixins的中文意思是混入,就是在类中混入其他功能。mixins弥补了接口和继承的不足,继承只能单继承,而接口无法复用实现,mixins却可以多混入并且能利用到混入类的具体实现
在Dart中可以使用mixins实现类似多继承的功能
因为mixins使用的条件,随着Dart版本一直在变,这里讲的是Dart2.x中使用mixins的条件:
1、作为mixins的类只能继承自Object,不能继承其他类
2、作为mixins的类不能有构造函数
3、一个类可以mixins多个mixins类
4、mixins绝不是继承,也不是接口,而是一种全新的特性
5.mixins的类型就是其超类的子类型
with关键字,后跟一个或多个mixin或者普通类。
on关键字:要指定只有某些特定类型可以使用mixin,使用on来指定所需的超类或者mixin,可以让你编写的mixin可以调用它未定义的方法,并且可以使用super像继承一样调用父类方法。
extension关键字(扩展):https://www.jianshu.com/p/f9d00020b3a5
==================Dart中扩展extension关键字 start============================
例子:
enum Month { jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec }
extension MonthExtension on Month {
int get value => this.index + 1;
String get cn =>
[
"一",
"二",
"三",
"四",
"五",
"六",
"七",
"八",
"九",
"十",
"十一",
"十二"
][this.index] +
"月";
String get eng => [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
][this.index];
}
使用:
final m = Month.jan;
print('value: ${m.value},cn: ${m.cn},eng: ${m.eng}');
打印结果:value: 1,cn: 一月,eng: Jan
==================Dart中扩展extension关键字 end============================
operator关键字:它和运算符(如=)一起使用,表示一个 运算符重载函数,在理解时可将operator和运算符(如operator=)视为一个函数名。
Dart
中没有接口,类都可以作为接口,把某个类当做接口实现时,只需要使用 implements
,然后复写父类方法即可。
Dart中抽象类: Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
1、抽象类通过abstract 关键字来定义
2、Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。
3、如果子类继承抽象类必须得实现里面的抽象方法
4、如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。
5、抽象类不能被实例化,只有继承它的子类可以
extends抽象类 和 implements的区别:
1、如果要复用抽象类里面的方法,并且要用抽象方法约束子类的话我们就用extends继承抽象类,子类里面不一定需要重写非抽象方法
2、如果只是把抽象类当做标准的话我们就用implements实现抽象类,子类必须得实现类里面定义的所有属性和方法
Datr中的多态:
子类的实例赋值给父类的引用。
多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
dart中的接口:
普通类或抽象类都可以作为接口被实现。建议使用抽象类定义接口。
同样使用implements关键字进行实现。
实现该接口,必须得实现类里面定义的所有属性和方法
继承(关键字 extends)单继承
混入 mixins (关键字 with)多混入
接口实现(关键字 implements)
这三种关系可以同时存在,但是有前后顺序:
extends -> mixins -> implements
extens在前,mixins在中间,implements最后,接下来看具体的例子。
参考链接:https://www.jianshu.com/p/dd11429ba80e
1、Flutter 和 React Native 不同主要在于 Flutter UI是直接通过 skia 渲染的 ,而 React Native 是将 js 中的控件转化为原生控件,通过原生去渲染的 。
2、Flutter 中存在 Widget
、 Element
、RenderObject
、Layer
四棵树。详情参考:Flutter的核心渲染模块三棵树_ailinghao的博客-CSDN博客_flutter 三棵树
3、Flutter 中默认主要通过 runtimeType
和 key
判断更新:
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
4、Flutter 中 InheritedWidget
一般用于状态共享,如Theme
、Localizations
、 MediaQuery
等,都是通过它实现共享状态,这样我们可以通过 context
去获取共享的状态,比如 ThemeData theme = Theme.of(context);
在
Element
的inheritFromWidgetOfExactType
方法实现里,有一个Map
的对象。_inheritedWidgets
_inheritedWidgets
一般情况下是空的,只有当父控件是InheritedWidget
或者本身是InheritedWidgets
时才会有被初始化,而当父控件是InheritedWidget
时,这个Map
会被一级一级往下传递与合并 。所以当我们通过
context
调用inheritFromWidgetOfExactType
时,就可以往上查找到父控件的Widget
。
5、flutter中的生命周期详情参考:flutter中state生命周期和app生命周期_ailinghao的博客-CSDN博客
6、Flutter 中 runApp
启动入口其实是一个 WidgetsFlutterBinding
,它主要是通过 BindingBase
的子类 GestureBinding
、ServicesBinding
、 SchedulerBinding
、PaintingBinding
、SemanticsBinding
、 RendererBinding
、WidgetsBinding
等,通过 mixins
的组合而成的。
7、Flutter 中的 Dart 的线程是以事件循环和消息队列的形式存在,包含两个任务队列,一个是 microtask 内部队列,一个是 event 外部队列,而 microtask 的优先级又高于 event 。
因为 microtask 的优先级又高于 event, 同时会阻塞event 队列,所以如果 microtask 太多就可能会对触摸、绘制等外部事件造成阻塞卡顿哦。
Flutter 中存在四大线程,分别为 UI Runner
、GPU Runner
、IO Runner
, Platform Runner
(原生主线程) ,同时在 Flutter 中可以通过 isolate
或者 compute
执行真正的跨线程异步操作。
8、Flutter 的 Debug 下是 JIT 模式,release下是AOT模式。
9、PlatformView
Flutter 中通过 PlatformView
可以嵌套原生 View
到 Flutter
UI 中,这里面其实是使用了 Presentation
+ VirtualDisplay
+ Surface
等实现的,大致原理就是:
使用了类似副屏显示的技术,VirtualDisplay
类代表一个虚拟显示器,调用 DisplayManager
的 createVirtualDisplay()
方法,将虚拟显示器的内容渲染在一个 Surface
控件上,然后将 Surface
的 id 通知给 Dart,让 engine 绘制时,在内存中找到对应的 Surface
画面内存数据,然后绘制出来。 实时控件截图渲染显示技术。
Flutter 中可以通过 Platform Channel
让 Dart 代码和原生代码通信的:
BasicMessageChannel
:用于传递字符串和半结构化的信息。
MethodChannel
:用于传递方法调用(method invocation)。
EventChanne
l: 用于数据流(event streams)的通信。
同时 Platform Channel
并非是线程安全的 ,更多详细可查阅闲鱼技术的 《深入理解Flutter Platform Channel》
其中基础数据类型映射如下:
Android 中 Flutter
默认启动时会在 FlutterActivityDelegate.java
中读取 AndroidManifset.xml 内 meta-data
标签,其中 io.flutter.app.android.SplashScreenUntilFirstFrame
标志位如果为 ture ,就会启动 Splash 画面效果(类似IOS的启动页面)。
启动时原生代码会读取 android.R.attr.windowBackground
得到指定的 Drawable
, 用于显示启动闪屏效果,之后并且通过 flutterView.addFirstFrameListener
,在onFirstFrame
中移除闪屏。
直接子类主要有:LocalKey
跟 GlobalKey
。
LocalKey 是增量算法的核心,决定哪个Element要保留,哪个Element要删除。以下是LocalKey的三个子类。
ValueKey
:以值作为参数(数字、字符串);:以对象作为参数
;:创建唯一标识
;容器左上角和右下角有弧度
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.zero,
bottomLeft: Radius.zero,
bottomRight: Radius.circular(20.0)),
)
容器左右两边有弧度
borderRadius:BorderRadius.all(Radius.circular(Dimens.rgap_dp25))
容器底部加根线
decoration: BoxDecoration(
color:Colors.white,
border: Border(
bottom: BorderSide(width:0.5,color:Colors.black12)
)
)
使用decoration实现颜色的渐变(左右渐变)
decoration: BoxDecoration(
gradient: LinearGradient(
begin: isOrientationLeftRight
? Alignment.centerLeft
: Alignment.topCenter,
end: isOrientationLeftRight
? Alignment.centerRight
: Alignment.bottomCenter,
colors: [gradientStart, gradientEnd]),
/*阴影设置
boxShadow: [
new BoxShadow(
color: Colors.grey[500],
blurRadius: 20.0,
spreadRadius: 1.0,
)
]*/
),
ffi基础学习:09、Flutter FFI Dart Native API_eieihihi的专栏-CSDN博客
代码例子: NativeDemo: flutter ffi 调用demo
Flutter FFI实践_落叶挽歌的博客-CSDN博客
图片压缩 TinyPNG – Compress WebP, PNG and JPEG images intelligently
今天是第几周,星期几,今天是2022年的第几周-万年历
10月Flutter最新学习资料汇总 - 贾鹏辉的技术博客官网|CrazyCodeBoy|Devio|专注移动技术开发(Android&IOS)、Flutter开发、Flutter教程、React Native开发、React Native教程、React Native博客
Flutter 123: 图解简易 GroupList 二级分组列表 - 简书
flutter例子
Flutter 集录指南FlutterUnit. https://github.com/toly1994328/FlutterUnit
flutter例子 https://github.com/syncfusion/flutter-examples
飞猪团队 :https://github.com/Fliggy-Mobile
飞猪团队陈冰:https://github.com/chenBingX
flutter个人技术文章 Nayuta 的个人主页 - 文章 - 掘金
(Flutter Widgets 大全)老孟博客(在线阅读地址):http://laomengit.com/flutter/widgets/widgets_structure.html
老孟 https://github.com/LaoMengFlutter/flutter-do
Flutter | 老孟
https://github.com/smartbackme
flutter实战电子书。 https://book.flutterchina.club/
flutter桌面支持官方网址:https://flutter.dev/desktop
flutter中文开发者网站 flutter.cn
flutter中文网: https://flutterchina.club/flutter-for-android/
dart中文网: https://www.dartcn.com/guides/language/language-tour
dart学习(可能要):http://dart.goodev.org/guides/language/language-tour
技术胖的博客地址:https://jspang.com
flutter库:https://pub.dartlang.org/ https://pub.flutter-io.cn/
将 Flutter 代码部署到 Web 端 https://blog.csdn.net/weixin_39901213/article/details/111213201
wanandroid:https://www.wanandroid.com/
codekk:http://p.codekk.com/
技术周报:https://www.androidweekly.cn/
在线作图:https://www.processon.com/
前端vant控件 https://youzan.github.io/vant/#/zh-CN/skeleton
接口测试:https://getman.cn/
leancloud后端云支持 https://www.leancloud.cn
咸鱼技术简书 https://www.jianshu.com/u/cf5c0e4b1111
唯鹿github地址: https://github.com/simplezhli
图片类
阿里巴巴矢量图标库
materialdesign。 https://material.io/resources/icons/?style=sharp
AndroidAssetStudio http://romannurik.github.io/AndroidAssetStudio/
图标工厂 https://icon.wuruihong.com/
hotpot https://hotpot.ai/
百度地图拾取坐标系统 http://api.map.baidu.com/lbsapi/getpoint/index.html
工具
typora 打开md格式的文件
设计模式https://www.runoob.com/design-pattern/factory-pattern.html
开眼快创Flutter实践:http://w4lle.com/2021/04/12/kidea-flutter/
开源项目
停车场系统。https://github.com/981011512/--
flutter项目
wechat_flutter Flutter版本微信,一个优秀的Flutter即时通讯IM开源库 https://github.com/fluttercandies/wechat_flutter
一款开源习惯打卡APP,动画为主:
Flutter 超完整的开源项目 https://github.com/CarGuo/gsy_github_app_flutter
Flutter 开源项目 FlutterApp https://github.com/shichunlei/flutter_app
Flutter 开源项目 https://github.com/iotjin/jh_flutter_demo
Flutter 练习项目(包括集成测试、可访问性测试)。内含完整UI设计图,更贴近真实项目的练习https://github.com/simplezhli/flutter_deer
flutter_deer 问题总结 https://segmentfault.com/a/1190000020329714?utm_source=tag-newest
Flutter使用的一些骚操作。https://github.com/CNAD666/flutter_use
Flutter 三方库使用和技术文章 https://github.com/jiang111/flutter_code
基于Google Flutter的WanAndroid客户端https://github.com/Sky24n/flutter_wanandroid
flutter项目。 https://github.com/Sky24n/Moss
仿阿里 Flutter-go v1.0 效果,并使用 玩Android 站点提供的 API 实现的 Flutter App
博客说明:https://longyi.blog.csdn.net/article/details/107045022
源码链接 https://github.com/YGragon/Flutter-WanAndroid
flutter开源项目
https://mp.weixin.qq.com/s/fQNK_4Lm4_DcCueA8V01Ug
https://github.com/cnad666/flutter_use
git clone [email protected]:cnad666/flutter_use.git
git clone [email protected]:toly1994328/flutterunit.git
git clone https://github.com/sunyongsheng/Allpass.git
git clone https://gitee.com/shizidada/moose_app
git clone [email protected]:ertcs/smart_home.git
Flutter仿京东客户端开源项目 https://www.jianshu.com/p/60588d6a076b
Flutter仿京东APP https://github.com/DiscoverForever/learn_flutter
flutter工具类https://github.com/Cheney2006/flutter_utils/tree/master/lib (相关博客https://www.cnblogs.com/maqingyuan/p/13656283.html)
flutter项目 https://www.jianshu.com/p/56b9cccf29f0
Flutter 仿携程网App https://github.com/wkl007/flutter_trip
flutterapp开发常用的轮子分享 https://github.com/826327700/flutter_plugins_demo
Flutter面试题汇总 Flutter面试题汇总 - 简书
分析可能原因:与布局的约束和尺寸有关,
GestureDetector(
onTap: () {
setState(() {
_expand = !_expand;
});
},
child: Container(
alignment: Alignment.center,
width: double.infinity,
color: Colors.white, //添加颜色后点击区域宽度为屏幕宽度,不添加点击区域只有图片本身
child: ImageLoadView(
_expand ? 'ic_arrow_up_gray' : 'ic_arrow_down',
imageType: ImageType.assets,
width: radiusDimens(25),
height: radiusDimens(25),
),
),
)