Fluter 2.0
已经发布了一段时间了,其中一项就是包含 Dart 2.12
的稳定版,完全支持空安全
声明。作为一个进步的 Flutter 组织 , 组织的小伙伴也在第一时间支持了空安全。
组织发布的组件: https://pub.flutter- io.cn/publishers/fluttercandies.com/packages 组织支持空安全的进度: https://github.com/fluttercandies/flutter_candies/issues/5
以下的组件均已支持空安全,这里只会做简单的介绍,具体使用方法,请到各组件下地址查看。
Adaptation,用于屏幕适配的组件,你只需要设置设计稿的宽度,其他的尺寸直接按照设计稿填写即可。当然这种适配方式其实是不推荐的,正如作者所言。
用户使用更大的屏幕是为了接收更多的信息, 而不是看到更大的字 基于这个观点, 我个人建议使用文字流式, 图片宽高比, 控件弹性的方案来做 但是很多初学者对于这个原则很难把握, 而等比例放大比较容易理解, 所以我写了这个库
import 'package:adaptation/adaptation.dart';
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
builder: (context, widget) {
return Adaptation(
child: widget,
designWidth: 375, // 你的设计稿宽度
);
},
);
assets_generator,用于自动生成 assets
配置 (yaml
) 以及 consts
的工具,支持单项目和多模块。
-h, --[no-]help 显示帮助信息
-p, --path Flutter 项目的根路径
(默认 ".")
-f, --folder assets 文件夹的名字
(默认 "assets")
-w, --[no-]watch 是否继续监听 assets 的变化
(默认 开启)
-t, --type pubsepec.yaml 生成配置的类型
"d" 代表以文件夹方式生成 "- assets/images/"
"f" 代表以文件方式生成 "- assets/images/xxx.jpg"
(默认 "d")
-s, --[no-]save 是否保存命令到本地
如果执行 "agen" 不带任何命令,将优先使用本地的命令进行执行
-o, --out const 类放置的位置
(默认放置在 "lib" 下面)
-r, --rule consts 的名字的命名规范
"lwu"(小写带下划线) : "assets_images_xxx_jpg"
"uwu"(大写带下划线) : "ASSETS_IMAGES_XXX_JPG"
"lcc"(小驼峰) : "assetsImagesXxxJpg"
(默认 "lwu")
-c, --class const 类的名字
(默认 "Assets")
--const-ignore 使用正则表达式忽略一些const(不是全部const都希望生成)
assets_generator.gif
ExtendedImage,集众多功能为一体的图片组件,包括以下主要功能:
| | ---|---|--- zoom.gif | slide.gif | photo_view.gif editor.gif 9e93eba3cc614ddbb818a3a445a41a99_tplv-k3u1fbpfcp-zoom-1 (1).gif | 2097626b7d0a406ab769cb528a31388e_tplv-k3u1fbpfcp-zoom-1.gif
9e93eba3cc614ddbb818a3a445a41a99_tplv-k3u1fbpfcp-zoom-1 (1).gif
ExtendedList,针对官方 Listview
和 GirdView
做的扩展组件,包括以下主要功能:
Viewport
中元素变化| ---|---
gridview.gif
|
chat_list.gif
ExtendedNestedScrollView,主要解决官方 NestedScrollView
存在的2个问题。
NestedScrollView
的 Header 中不能处理多个 pinned
为 true
的元素的问题。https://github.com/flutter/flutter/issues/22393NestedScrollView
的 Body 中列表滚动会互相影响的问题。https://github.com/flutter/flutter/issues/21868ExtendedSliver,对 Sliver
组件的扩展,主要包括以下功能:
SliverPinnedPersistentHeader
,跟官方的SliverPersistentHeader(pinned: true)
一样的效果, 不同的是你不需要去设置 minExtent
和 maxExtent
。因为大部分场景下面,我们是无法提前知道 minExtent
和 maxExtent
。SliverPinnedToBoxAdapter
,可以通过它轻松创建一个置顶的元素,当 child
没有 layout
之前,你没法知道 child
的实际大小,这将是非常有用的组件。ExtendedSliverAppbar
,你可以创建一个跟 SliverAppbar
一样效果的组件,而不用去关心 expandedHeight
。extended_sliver.gif
ExtendedTabs,对 TabBarView
组件的扩展,主要包括以下功能:
TabBarView
嵌套的时候,无法连贯切换的问题CarouselIndicator
和 ColorTabIndicator
| ---|---
link.gif
|
scrollDirection.gif
ExtendedText,针对 Text
组件的扩展,主要包括以下功能:
InlineSpan
。BackgroundTextSpan
自定文字背景,处理圆角或者中英文背景高度不一致的问题。ExtendedWidgetSpan
支持选择和复制, https://github.com/flutter/flutter/issues/38474 .TextOverflowWidget
自定义文本溢出效果, https://github.com/flutter/flutter/issues/26748 。| ---|---
special_text.jpg
|
overflow.jpg
background.png
|
selection.gif
ExtendedTextField,针对 TextField
组件的扩展,主要包括以下功能:
InlineSpan
。ExtendedWidgetSpan
支持输入框中插入任何 Widget
,比如表情图片。ExtendedWidgetSpan
支持选择和复制, https://github.com/flutter/flutter/issues/30688 。| ---|---
extended_text_field.gif
extended_text_field_image.gif
custom_toolbar.gif
|
widget_span.gif
ff_annotation_route,通过注解生成路由映射,统一处理路由,支持 Navigator 1.0
和 Navigator 2.0
。
pub global activate ff_annotation_route
dependencies:
# 在子模块中引入
ff_annotation_route_core: any
# 在根项目引入,包括一些帮助类以及 ff_annotation_route_core
ff_annotation_route_library: any
工具会自动处理带参数的构造,不需要做特殊处理。唯一需要注意的是,你需要设置 argumentImports
来为 class/enum
的参数提供 import
地址。现在你也可以使用 @FFArgumentImport()
注释来替代.
@FFArgumentImport('hide TestMode2')
import 'package:example1/src/model/test_model.dart';
@FFArgumentImport()
import 'package:example1/src/model/test_model1.dart' hide TestMode3;
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
@FFRoute(
name: 'flutterCandies://testPageE',
routeName: 'testPageE',
description: 'Show how to push new page with arguments(class)',
// argumentImports are still work for some cases which you can't use @FFArgumentImport()
// argumentImports: <String>[
// 'import \'package:example1/src/model/test_model.dart\';',
// 'import \'package:example1/src/model/test_model1.dart\';',
// ],
exts: <String, dynamic>{
'group': 'Complex',
'order': 1,
},
)
class TestPageE extends StatelessWidget {
const TestPageE({
this.testMode = const TestMode(
id: 2,
isTest: false,
),
this.testMode1,
});
factory TestPageE.deafult() => TestPageE(
testMode: TestMode.deafult(),
);
factory TestPageE.required({@required TestMode testMode}) => TestPageE(
testMode: testMode,
);
final TestMode testMode;
final TestMode1 testMode1;
}
ff_route <command> [arguments]
,全部命令如下:-h, --[no-]help 帮助信息。
-p, --path 执行命令的目录,默认当前目录。
-o, --output route 和 helper 文件的输出目录路径,路径相对于主项目的 lib 文件夹。
-n, --name 路由常量类的名称,默认为 `Routes`。
-g, --git 扫描 git 引用的 package,你需要指定 package 的名字,多个用 `,` 分开
--routes-file-output routes 文件的输出目录路径,路径相对于主项目的lib文件夹
--const-ignore 使用正则表达式忽略一些const(不是全部const都希望生成)
--[no-]route-constants 是否在根项目中的 `xxx_route.dart` 生成全部路由的静态常量
--[no-]package 这个是否是一个 package
--[no-]supper-arguments 是否生成路由参数帮助类
-s, --[no-]save 是否保存命令到本地。如果保存了,下一次就只需要执行 `ff_route` 就可以了。
--[no-]null-safety 是否支持空安全,默认 `true`
MaterialApp
的 onGenerateRoute
回调import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
import 'package:flutter/material.dart';
import 'example_route.dart';
import 'example_routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ff_annotation_route demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: Routes.fluttercandiesMainpage,
onGenerateRoute: (RouteSettings settings) {
return onGenerateRoute(
settings: settings,
getRouteSettings: getRouteSettings,
routeSettingsWrapper: (FFRouteSettings ffRouteSettings) {
if (ffRouteSettings.name == Routes.fluttercandiesMainpage ||
ffRouteSettings.name ==
Routes.fluttercandiesDemogrouppage.name) {
return ffRouteSettings;
}
return ffRouteSettings.copyWith(
widget: CommonWidget(
child: ffRouteSettings.widget,
title: ffRouteSettings.routeName,
));
},
);
},
);
}
}
Navigator.pushNamed(
context,
Routes.flutterCandiesTestPageE.name,
arguments: Routes.flutterCandiesTestPageE.requiredC(
testMode: const TestMode(
id: 100,
isTest: true,
),
),
);
DraggableContainer,可拖拽容器,支持元素移动动画效果,主要包括以下功能:
image
ImageEditor,强大的原生图片处理库,主要包括以下功能:
editor.gif
SmartDialog,一种更优雅的Dialog 解决方案,主要解决了系统自带的Dialog的一些问题:
smartDialog
AssetPicker,对标微信的多选资源选择器,99%接近于原生微信的操作,主要包括以下功能:
photo_manager
)CameraPicker,对标微信的视频资源选择器,99%接近于原生微信的操作,主要包括以下功能:
image
|
image
---|---
image
|
image
JsonToDart,强大的 JsonToDart
工具,主要包括以下功能:
平台 | 描述 | 地址 |
---|---|---|
Windows | Flutter for Windows | |
https://gitee.com/zmtzawqlp/JsonToDart/releases/ | ||
Macos | Flutter for Macos | |
https://gitee.com/zmtzawqlp/JsonToDart/releases/ | ||
Web | Flutter for Web | |
https://zmtzawqlp.gitee.io/jsontodart/ | ||
微软商店 | 功能未同步,以后会替换成 [Flutter for | |
UWP](https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2Fflutter%2Fflutter%2Fissues%2F14967) |
| https://www.microsoft.com/store/apps/9NBRW9451QSR
JsonToDart.gif
LikeButton,仿推特点赞效果,支持数字动画效果。
image
LoadingMoreList,支持各种布局的增量加载列表,主要包括以下功能:
Viewport
元素| | ---|---|---
listview.gif
|
multiple_sliver.gif
error.gif
custom_indicator.gif
|
nested_scrollView.gif
known_sized.gif
PullToRefreshNotification,灵活的自定义下拉刷新组件,可以创造出任意的下拉刷新样式。
| ---|---
appbar.gif
|
header.gif
image.gif
|
candies.gif
RippleBackdropAnimatePage,骚气十足的模糊动画,只需要几行代码就能帮你实现。
image
WPopupMenu,目前最好用的仿微信聊天长按弹出框。
image
WaterfallFlow,高性能的瀑布流布局,https://github.com/flutter/flutter/issues/40856 。
GridView
一样的 api
Viewport
元素| ---|---
random_sized.gif
|
custom_scrollView.gif
known_sized.gif
|
variable_sized.gif
感谢 Flutter & Dart
文档中国本地化 全球遮天团
为我们提供了完整准确的文档,https://dart.cn/null- safety/migration- guide ,空安全
迁移大概有下面几个步骤:
flutter pub outdated --mode=null-safety
,检查自己项目依赖的库是否都支持空安全。dart migrate --apply-changes
。不加 --apply-changes
的话,会有一个浏览器地址,你打开之后,可以在浏览器中进行修改。我一般还是习惯在直接 --apply-changes
之后直接在 vscode
中进行修改。执行完毕之后,你的 Dart SDK
版本会自动改为大于2.12.0
。(注意,执行 dart migrate 命令必须确保 SDK
是小于 2.12.0
的)environment:
sdk: '>=2.12.0 <3.0.0'
错误
,请先查看完 https://dart.cn/null-safety 之后,根据自己的业务场景对代码进行更正。非空
List<T>
的影响是非常大的。不能对非空的列表设置更大的长度 List 的 length getter 也有一个对应的 setter,这一点鲜为人知。您可以对列表设置一个较短的长度,从而截断它。您也可以对列表设置一个更长的长度,从而使用未初始化的元素填充它。 如果您对一个非空的列表做了这样的操作,在访问未初始化的元素时,就与空安全的健全性发生了冲突。为了防止意外发生,现在对一个非空类型的数组调用调用 length setter, 并且 准备设置一个更长的长度时,会在运行时抛出一个异常。您仍然可以对任何类型的列表进行截断,也可以对一个可空类型的列表进行填充。 如果您自定义了列表的类型,例如继承了 ListBase 或者混入了 ListMixin,那么这项改动可能会造成较大的影响。以上的两种类型都提供了 insert() 的实现,通过设置长度,为插入的元素提供空间。在空安全中这样做可能会出现错误,所以我们将它们的 insert() 实现改为了 add()。现在您自定义的列表应该继承 add() 方法 方法。
下面我们来跟一波可空
列表在做 add
操作时候的流程,来理解下文档所说的意思。
类 | 位置 |
---|---|
list.dart | bin/cache/dart-sdk/lib/collection/list.dart |
growable_array.dart | `bin/cache/dart- |
sdk/lib/_internal/vm/lib/growable_array.dart` | |
array.dart | bin/cache/dart-sdk/lib/_internal/vm/lib/array.dart |
ListMixin.add (dart:collection/list.dart:278)
// List interface.
void add(E element) {
// This implementation only works for lists which allow `null`
// as element.
this[this.length++] = element;
}
List.length= (dart:core-patch/growable_array.dart:227)
void set length(int new_length) {
if (new_length > length) {
// Verify that element type is nullable.
// 官方在这里做了判断
null as T;
if (new_length > _capacity) {
_grow(new_length);
}
_setLength(new_length);
return;
}
final int new_capacity = new_length;
List._grow (dart:core-patch/growable_array.dart:362)
void _grow(int new_capacity) {
// 创建了一个长度为 new_capacity 的数组,并且用 null 填充
var newData = _allocateData(new_capacity);
// This is a work-around for dartbug.com/30090: array-bound-check
// generalization causes excessive deoptimizations because it
// hoists CheckArrayBound(i, ...) out of the loop below and turns it
// into CheckArrayBound(length - 1, ...). Which deoptimizes
// if length == 0. However the loop itself does not execute
// if length == 0.
// 从旧列表中复制数据
if (length > 0) {
for (int i = 0; i < length; i++) {
newData[i] = this[i];
}
}
// 通知引擎替换新数据
_setData(newData);
}
List._allocateData (dart:core-patch/growable_array.dart:349)
以及 _List (dart:core-patch/array.dart.dart:13)
static _List _allocateData(int capacity) {
if (capacity == 0) {
// Use shared empty list as backing.
return _emptyList;
}
// Round up size to the next odd number, since this is free
// because of alignment requirements of the GC.
// (dart:core-patch/array.dart.dart:13)
return new _List(capacity | 1);
}
@pragma("vm:recognized", "graph-intrinsic")
void _setData(_List array) native "GrowableList_setData";
@pragma("vm:recognized", "graph-intrinsic")
void _setLength(int new_length) native "GrowableList_setLength";
image.png
在第三步
中,会返回元素为 null
的列表,所以在空安全的情况下,列表
操作中需要做以下改动。
List<int> list = List<int>();
改为 List<int> list = <int>[];
List<int> list = List<int>(1);
改为 List<int> list = List<int>.filled(1, 0);
ListBase
或者混入了 ListMixin
,你需要重写 add()
方法,否则在第二步
中就会报错。完整代码
@override void add(T element) { _array.add(element); }坐和放宽
对于每次的大版本更新,不要着急升级,特别是你的项目引用了三方组件。三方开源作者是不大可能有时间立刻就更新,订阅一下作者的更新计划,静静等待。一般 stable 版本发布之后都会有热修复版本。如果你是新手,请坐和放宽
,静待大佬们发现和解决点一些重大问题之后再更新。
image.png
学会使用 pub.dev
空安全的组件有很明显的标志 Null safety
。打开 Versions
一签,通过 Min Dart SDK
很容易就看出组件是从哪个版本开始支持空安全的,比如 extended_image
从 3.0.0
版本支持空安全。
image.png
另外,有些组件还提供了 Prerelease versions
。比如 extended_image
还提供了非空安全版本. 当然 Prerelease versions
也可能是预览版,修复紧急问题(一些用户使用 Flutter master/dev/beta
分支,该分支可能会有一些 api
的 breaking change
),作者会发布预览版来满足这部分人群。
image.png
每个人都从萌新而来,爱护萌新,但也不应该纵容巨婴。
Flutter
,这应该是你必看的网站。截屏2021-04-10 下午2.57.54.png
issue
,让官方知晓,为其他人节约时间,提供思路。不要担心你的英文水平,只有多写,多练习,才能更好。Flutter
文章最多的一个网站了,对于英文不好的小伙伴,有中文的各种各样的 Flutter
相关文章也是极好的。 qq群:181398081,如果通过上面的方式,你还是没法解决问题,你可以在 qq群
里面提问,很荣幸群里有一群热心的群友,互相帮助,互相学习。
image
2岁的糖果
不知不觉,糖果
已经 2岁
了,Flutter
也 2.0
了。感谢 糖果
的小伙伴,对开源组件的持续支持。从 Flutter Candies 一桶天下 到现在又一年了,组织也在不断地壮大。欢迎更多的小伙伴都加入进来,一起为 Flutter
社区添砖加瓦。
截屏.png
致糖果们
从 2019年2月14日
孤单一个人,到现在的 2000
人,感谢每个糖果的支持,感谢积极回复问题的糖果们,感谢智能憨憨的群机器人。如果你喜欢分享,请加入我们;如果你需要别人的分享,也请加入我们。
image.png
爱Flutter
,爱糖果
很开心你能阅读到这里,爱Flutter
,爱糖果
,欢迎加入 Flutter Candies ,一起生产可爱的 Flutter小糖果
QQ群:181398081。最后放上 Flutter Candies 全家桶,真香。
flutter_candies_logo.png
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。