- Flutter 教程
- Flutter - 首页
- Flutter - 简介
- Flutter - 安装
- 在 Android Studio 中创建简单的应用程序
- Flutter - 架构应用程序
- Dart 编程入门
- Flutter - Widget 入门
- Flutter - 布局入门
- Flutter - 手势入门
- Flutter - 状态管理
- Flutter - 动画
- Flutter - 编写 Android 特定代码
- Flutter - 编写 iOS 特定代码
- Flutter - 包入门
- Flutter - 访问 REST API
- Flutter - 数据库概念
- Flutter - 国际化
- Flutter - 测试
- Flutter - 部署
- Flutter - 开发工具
- Flutter - 编写高级应用程序
- Flutter - 结论
- Flutter 有用资源
- Flutter - 快速指南
- Flutter - 有用资源
- Flutter - 讨论
Flutter - 应用状态
应用程序状态 - scoped_model
Flutter 提供了一种简单的方法来使用 scoped_model 包管理应用程序的状态。Flutter 包仅仅是可重用功能的库。我们将在接下来的章节中详细了解 Flutter 包。
scoped_model 提供了三个主要的类来支持应用程序中强大的状态管理,这里将详细讨论它们:
模型
模型封装了应用程序的状态。我们可以根据需要使用任意多个模型(通过继承 Model 类)来维护应用程序的状态。它有一个方法 notifyListeners,每当模型状态发生变化时都需要调用它。notifyListeners 将执行更新 UI 的必要操作。
class Product extends Model { final String name; final String description; final int price; final String image; int rating; Product(this.name, this.description, this.price, this.image, this.rating); factory Product.fromMap(Map<String, dynamic> json) { return Product( json['name'], json['description'], json['price'], json['image'], json['rating'], ); } void updateRating(int myRating) { rating = myRating; notifyListeners(); } }
ScopedModel
ScopedModel 是一个 Widget,它持有给定的模型,然后在请求时将其传递给所有后代 Widget。如果需要多个模型,则需要嵌套 ScopedModel。
单个模型
ScopedModel<Product>( model: item, child: AnyWidget() )
多个模型
ScopedModel<Product>( model: item1, child: ScopedModel<Product>( model: item2, child: AnyWidget(), ), )
ScopedModel.of 是一个用于获取 ScopedModel 底层模型的方法。即使模型将要发生变化,但如果不需要 UI 更改,也可以使用它。以下操作不会更改产品的 UI(评分)。
ScopedModel.of<Product>(context).updateRating(2);
ScopedModelDescendant
ScopedModelDescendant 是一个 Widget,它从上层 Widget ScopedModel 获取模型,并在模型发生变化时构建其用户界面。
ScopedModelDescendant 具有两个属性:builder 和 child。child 是 UI 的一部分,不会发生变化,并将传递给 builder。builder 接受一个具有三个参数的函数:
content - ScopedModelDescendant 传递应用程序的上下文。
child - UI 的一部分,不会根据模型发生变化。
model - 该实例的实际模型。
return ScopedModelDescendant<ProductModel>( builder: (context, child, cart) => { ... Actual UI ... }, child: PartOfTheUI(), );
让我们将之前的示例更改为使用 scoped_model 而不是 StatefulWidget
在 Android Studio 中创建一个新的 Flutter 应用程序,命名为 product_scoped_model_app
将默认的启动代码 (main.dart) 替换为我们的 product_state_app 代码
将 assets 文件夹从 product_nav_app 复制到 product_rest_app,并在 pubspec.yaml 文件中添加 assets
flutter: assets: - assets/appimages/floppy.png - assets/appimages/iphone.png - assets/appimages/laptop.png - assets/appimages/pendrive.png - assets/appimages/pixel.png - assets/appimages/tablet.png
在 pubspec.yaml 文件中配置 scoped_model 包,如下所示:
dependencies: scoped_model: ^1.0.1
这里,您应该使用 http 包的最新版本
Android Studio 将提示 pubspec.yaml 已更新。
单击“获取依赖项”选项。Android Studio 将从互联网获取包并为应用程序正确配置它。
将默认的启动代码 (main.dart) 替换为我们的启动代码。
import 'package:flutter/material.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: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue,), home: MyHomePage(title: 'Product state demo home page'), ); } } class MyHomePage extends StatelessWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(this.title), ), body: Center( child: Text( 'Hello World', ) ), ); } }
在 main.dart 文件中导入 scoped_model 包。
import 'package:scoped_model/scoped_model.dart';
让我们创建一个 Product 类 Product.dart 来组织产品信息。
import 'package:scoped_model/scoped_model.dart'; class Product extends Model { final String name; final String description; final int price; final String image; int rating; Product(this.name, this.description, this.price, this.image, this.rating); factory Product.fromMap(Map<String, dynamic> json) { return Product( json['name'], json['description'], json['price'], json['image'], json['rating'], ); } void updateRating(int myRating) { rating = myRating; notifyListeners(); } }
这里,我们使用了 notifyListeners 在每次评分更改时更改 UI。
让我们在 Product 类中编写一个方法 getProducts 来生成我们的虚拟产品记录。
static List<Product> getProducts() { List<Product> items = <Product>[]; items.add( Product( "Pixel", "Pixel is the most feature-full phone ever", 800, "pixel.png", 0 ) ); items.add( Product( "Laptop", "Laptop is most productive development tool", 2000, "laptop.png", 0 ) ); items.add( Product( "Tablet", "Tablet is the most useful device ever for meeting", 1500, "tablet.png", 0 ) ); items.add( Product( "Pendrive", "Pendrive is useful storage medium", 100, "pendrive.png", 0 ) ); items.add( Product( "Floppy Drive", "Floppy drive is useful rescue storage medium", 20, "floppy.png", 0 ) ); return items; } import product.dart in main.dart import 'Product.dart';
让我们更改新的 Widget RatingBox 以支持 scoped_model 概念。
class RatingBox extends StatelessWidget { RatingBox({Key key, this.item}) : super(key: key); final Product item; Widget build(BuildContext context) { double _size = 20; print(item.rating); return Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.max, children: <Widget>[ Container( padding: EdgeInsets.all(0), child: IconButton( icon: ( item.rating >= 1 ? Icon( Icons.star, size: _size, ) : Icon( Icons.star_border, size: _size, ) ), color: Colors.red[500], onPressed: () => this.item.updateRating(1), iconSize: _size, ), ), Container( padding: EdgeInsets.all(0), child: IconButton( icon: (item.rating >= 2 ? Icon( Icons.star, size: _size, ) : Icon( Icons.star_border, size: _size, ) ), color: Colors.red[500], onPressed: () => this.item.updateRating(2), iconSize: _size, ), ), Container( padding: EdgeInsets.all(0), child: IconButton( icon: ( item.rating >= 3? Icon( Icons.star, size: _size, ) : Icon( Icons.star_border, size: _size, ) ), color: Colors.red[500], onPressed: () => this.item.updateRating(3), iconSize: _size, ), ), ], ); } }
这里,我们从 StatelessWidget 而不是 StatefulWidget 扩展了 RatingBox。此外,我们使用了 Product 模型的 updateRating 方法来设置评分。
让我们修改 ProductBox Widget 以与 Product、ScopedModel 和 ScopedModelDescendant 类一起工作。
class ProductBox extends StatelessWidget { ProductBox({Key key, this.item}) : super(key: key); final Product item; Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(2), height: 140, child: Card( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Image.asset("assets/appimages/" + this.item.image), Expanded( child: Container( padding: EdgeInsets.all(5), child: ScopedModel<Product>( model: this.item, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Text(this.item.name, style: TextStyle(fontWeight: FontWeight.bold)), Text(this.item.description), Text("Price: " + this.item.price.toString()), ScopedModelDescendant<Product>( builder: (context, child, item) { return RatingBox(item: item); } ) ], ) ) ) ) ] ), ) ); } }
这里,我们将 RatingBox Widget 包含在 ScopedModel 和 ScopedModelDescendant 中。
更改 MyHomePage Widget 以使用我们的 ProductBox Widget。
class MyHomePage extends StatelessWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; final items = Product.getProducts(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Product Navigation")), body: ListView.builder( itemCount: items.length, itemBuilder: (context, index) { return ProductBox(item: items[index]); }, ) ); } }
这里,我们使用了 ListView.builder 来动态构建产品列表。
应用程序的完整代码如下:
Product.dart import 'package:scoped_model/scoped_model.dart'; class Product extends Model { final String name; final String description; final int price; final String image; int rating; Product(this.name, this.description, this.price, this.image, this.rating); factory Product.fromMap(Map<String, dynamic> json) { return Product( json['name'], json['description'], json['price'], json['image'], json['rating'], );n } void cn "Laptop is most productive development tool", 2000, "laptop.png", 0)); items.add( Product( "Tablet"cnvn, "Tablet is the most useful device ever for meeting", 1500, "tablet.png", 0 ) ); items.add( Product( "Pendrive", "Pendrive is useful storage medium", 100, "pendrive.png", 0 ) ); items.add( Product( "Floppy Drive", "Floppy drive is useful rescue storage medium", 20, "floppy.png", 0 ) ) ; return items; } main.dart import 'package:flutter/material.dart'; import 'package:scoped_model/scoped_model.dart'; import 'Product.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: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Product state demo home page'), ); } } class MyHomePage extends StatelessWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; final items = Product.getProducts(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Product Navigation")), body: ListView.builder( itemCount: items.length, itemBuilder: (context, index) { return ProductBox(item: items[index]); }, ) ); } } class RatingBox extends StatelessWidget { RatingBox({Key key, this.item}) : super(key: key); final Product item; Widget build(BuildContext context) { double _size = 20; print(item.rating); return Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.max, children: <Widget>[ Container( padding: EdgeInsets.all(0), child: IconButton( icon: ( item.rating >= 1? Icon( Icons.star, size: _size, ) : Icon( Icons.star_border, size: _size, ) ), color: Colors.red[500], onPressed: () => this.item.updateRating(1), iconSize: _size, ), ), Container( padding: EdgeInsets.all(0), child: IconButton( icon: (item.rating >= 2 ? Icon( Icons.star, size: _size, ) : Icon( Icons.star_border, size: _size, ) ), color: Colors.red[500], onPressed: () => this.item.updateRating(2), iconSize: _size, ), ), Container( padding: EdgeInsets.all(0), child: IconButton( icon: ( item.rating >= 3 ? Icon( Icons.star, size: _size, ) : Icon( Icons.star_border, size: _size, ) ), color: Colors.red[500], onPressed: () => this.item.updateRating(3), iconSize: _size, ), ), ], ); } } class ProductBox extends StatelessWidget { ProductBox({Key key, this.item}) : super(key: key); final Product item; Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(2), height: 140, child: Card( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Image.asset("assets/appimages/" + this.item.image), Expanded( child: Container( padding: EdgeInsets.all(5), child: ScopedModel<Product>( model: this.item, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Text( this.item.name, style: TextStyle( fontWeight: FontWeight.bold ) ), Text(this.item.description), Text("Price: " + this.item.price.toString()), ScopedModelDescendant<Product>( builder: (context, child, item) { return RatingBox(item: item); } ) ], ) ) ) ) ] ), ) ); } }
最后,编译并运行应用程序以查看其结果。它将与之前的示例类似,只是应用程序使用了 scoped_model 概念。