- 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 概念。