c语言sscanf函数的用法是什么
243
2022-11-16
Flutter学习 功能型Widget
文章目录
1. WillPopScope
1.1 示例
2. InheritedWidget
2.1 didChangeDependencies2.2 深入了解 InheritedWidget
3. Provider
3.1 实现简易 Provider
3.1.1购物车示例
4. 主题 Theme5. ValueListenableBuilder
5.1 示例
6. 异步 UI 更新
1. WillPopScope
是导航返回拦截的组件, 类似于 Android 中封装的 onBackPress 方法,来看看它的构造函数:
class WillPopScope extends StatefulWidget { const WillPopScope({ Key? key, required this.child, required this.onWillPop, })
1.1 示例
2. InheritedWidget
用于数据共享的组件,提供了一种在 Widget 树中从上到下共享数据的方式,比如我们在应用的根 Widget 中通过 InheritedWidget 共享了一个数据,那我们可以在任意子 Widget 树中去获取该共享数据。
这个特性在一些需要整个 Widget 中共享数据的场景中非常方便。比如 Flutter 正是通过该组件来实现 共享应用主题 和 Locale 信息。
2.1 didChangeDependencies
在学习 StatefulWidget 时,我们提到了 State 对象有一个 didChangeDependencies 的回调,它会在 “依赖” 发生变化的时候被Flutter框架调用,而这个 “依赖” 就是 子Widget 是否使用了 父Widget 中 InheritedWidget 的数据,如果使用了,则代表 子Widget 有依赖, 如果没有则表示没有这种依赖。
这种机制可以使子组件所依赖的 InheritedWidget 变化时来更新自身, 比如主题、locale 等发生变化时,依赖其 子Widget 的 didChangeDEpendencies 方法就会被调用
下面来看下官方示例中, 计算器应用的 InheritedWidget 版本:
如果 _TestWidget 中没有使用 ShareDataWidget 中的数据,那么它的 didChangeDependencies() 将不会调用,因为没有依赖其数据。
didChangeDependencies 中可以做什么? 一般来说, 子 Widget 很少会重写该方法,因为在依赖改变后, Flutter 框架也会调用 build 方法重新构建组件树,但是如果需要在依赖改变后执行一些昂贵的操作,比如数据库存储或者网络库请求,这时最好的方式就是在此方法中执行,这样可以避免每次 build 都去执行这些昂贵的操作。
2.2 深入了解 InheritedWidget
如果我们只想在 _TestWidgetState 中引用 ShareDataWidget 的数据,却不希望 ShareDataWidget 发生变化时调用了 _TestWidgetState 的方法应该怎么办呢?
我们只需要改一下 ShareDataWidget.of() 的实现方式:
? of(BuildContext context) { // return context.dependOnInheritedWidgetOfExactType
唯一的改动是把 dependOnInheritedWidgetOfExactType 方法换成了 getElementForInheritedWidgetOfExactType ,他们有什么区别呢?我们来看下这两个方法的源码:
override InheritedElement? getElementForInheritedWidgetOfExactType
来看下 dependOnInheritedElement :
override InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) { assert(ancestor != null); _dependencies ??= HashSet
dependOnInheritedElement 方法主要是注册了依赖关系,加进到一个 HashSet 中。而 getElementForInheritedWidgetOfExactType() 不会。
那么就引入了一个新的问题: 实际上,我们只想更新子树中依赖了 ShareDataWidget 的子节点,而在调用了父组件(这里是 _InheritedWidgetTestRouteState)setState 方法必然会导致所有子节点build。 这会赵成不必要的浪费,而且可能会出现问题。
而 缓存数据 可以解决这个问题, 就是通过封装一个 StatefulWidget 将 子Widget 树缓存起来,下面就来实现一个 Provider 来演示。
3. Provider
Provider 包的思想是: 将需要跨组件共享的状态保存在 InheritedWidget 中,然后子组件引用 InheritedWidget , InheritedWidget 会绑定子组件产生依赖关系,然后当数据发生改变时,自动更新子孙组件。
为了加强理解,这里不直接看 Provider 实现,而是实习哪一个最小功能的 Provider
3.1 实现简易 Provider
这里引入泛型 ,便于外界能够保存更通用的数据
class InheritedProvider
第二步,我们来实现 “数据发生改变时该如何改变?”, 这里的做法是通过使用加监听器, Flutter 中有 ChangeNotifier ,继承自 Listenable,是一个发布-订阅者模式,通过 addListener、 removeListener 来添加监听者, 用 notifyListener 来触发监听器的回调。
所以我们将共享的状态放到一个 Model 类中,然后让它继承自 ChangeNotifier, 这样当共享状态改变时,只需要调用 notify 就可以通知订阅者,订阅者来重新构建 InheritedProvider 了:
class ChangeNotifierProvider
该类继承自 StatefulWidget,然后提供 of 方法供子类方便获取 Widget 树中的 InheritedProvider 中保存的共享状态, 下面来实现该类对应的 State 类:
class _ChangeNotifierProviderState
可以看到, _ChangeNotifierProviderState 类的主要作用是监听到共享状态改变时,重新构建 Widget 树。在 _ChangeNotiferProviderState 中调用 setState 方法, widget.child 始终是同一个,所以执行 build 时, InheritedProvider 的child引用的始终是同一个 子widget, 所以 widget.child 并不会重新 build , 这也就相当于对 child 进行了缓存,当然如果 ChangeNotifierProvider 的 父Widget 重新build 时, 则其传入的 child 可能会发生变化。
接下来我们用该组件实现一个 购物车示例。
3.1.1购物车示例
我们需要实现一个显示购物车中所有商品总价的功能,而这个价格显然就是我们想要共享的状态, 因为购物车的价格会随着商品的添加和移除而改变。
我们来定义一个 Item 类,用于表示商品信息:
class Item { Item(this.price, this.count); // 商品单价 double price; // 商品数量 int count; }
接着定义一个保存购物车内商品数据的 CartModel 类:
class CartModel extends ChangeNotifier { final List
这个 CartModel 就是我们需要跨组件共享的数据类型,最后我们写一个示例页面:
class _ProviderRouteState extends State
使用 Provider 后带来的好处有:
业务代码值关心数据更新,只需要更新 Model, UI就会自动更新,而不用在状态改变后去手动调用 setState 来显示刷新页面数据改变的消息传递被屏蔽了大型复杂场景下,使用全局共享变量会简化代码逻辑
4. 主题 Theme
ThemeData 用于保存 Material 组件库中的主题数据, 它包含了可以自定义的部分,我们可以通过 ThemeData 来自定义应用主题,在子组件中,我们可以通过 Theme.of 方法来获取当前的 ThemeData。
ThemeData 的可定义属性非常之多,下面截取一些常用的构造属性:
ThemeData({ Brightness? brightness, //深色还是浅色 MaterialColor? primarySwatch, //主题颜色样本,见下面介绍 Color? primaryColor, //主色,决定导航栏颜色 Color? cardColor, //卡片颜色 Color? dividerColor, //分割线颜色 ButtonThemeData buttonTheme, //按钮主题 Color dialogBackgroundColor,//对话框背景颜色 String fontFamily, //文字字体 TextTheme textTheme,// 字体主题,包括标题、body等文字样式 IconThemeData iconTheme, // Icon的默认样式 TargetPlatform platform, //指定平台,应用特定平台控件风格 ColorScheme? colorScheme, ...})
下面我们来实现一个路由换肤的功能:
class _ThemeRouteState extends State
效果如下:
我们可以通过局部主题覆盖全局主题,如果需要对整个应用换肤,可以修改 MaterialApp 的 theme
5. ValueListenableBuilder
InheritedWidget 提供了一种从上到下的数据共享方式,而有些场景并非从上到下传递,比如横向传递或者从下到上,为了解决这个问题, Flutter 提供了 ValueListenableBuilder 组件,它的功能是监听一个数据源,如果数据源发生了变化,则会重新执行其 builder。
定义为:
const ValueListenableBuilder({ Key? key, required this.valueListenable, required this.builder, this.child, })
valueListenable 表示一个可监听的数据源, 类型为 ValueListenable
5.1 示例
class _ValueListenableState extends State
因此使用建议是: 尽可能让 ValueListenableBuilder 只构建依赖数据源的 Widget, 这样可以缩小构建范围,也就是说 ValueListenableBuilder 的拆分粒度可以更细
6. 异步 UI 更新
很多时候我们会依赖一些异步数据来动态更新 UI,比如我们要先获取Http数据,然后获取数据过程中显示一个加载框,等获取到数据时我们再渲染页面,又比如想展示 Stream 的进度。 Flutter 则分别提供了 FutureBuilder 和 StreamBuilder 两个组件来快速实现这两个功能,示例比较简单,这里就不再列举了
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~