iT邦幫忙

2022 iThome 鐵人賽

DAY 16
0
自我挑戰組

30天學習flutter系列 第 16

16.flutter的狀態管理(四)

  • 分享至 

  • xImage
  •  

狀態管理:Provider

Provider封裝了InheritedWidget的功能,並讓我們能更容易地使用,而provider提供了我們:

  • 簡化資源分配/處置
  • 延遲加載
  • 大減少了每次創建新class的boilerplate
  • 更通俗的使用InheritedWidgets方法

基本使用測試

在使用前先重新建構我們project,將components資料夾改成widgets並重新命名裡面的檔案
https://ithelp.ithome.com.tw/upload/images/20221001/20108931cWNoodpSR6.png

安裝套件

pubspec.yaml

dependencies:
  provider: ^6.0.3

https://ithelp.ithome.com.tw/upload/images/20221001/20108931ZGdEqIDw6N.png

定義共享資料

我們創建models和states資料夾,並先創建todo model
https://ithelp.ithome.com.tw/upload/images/20221001/20108931S4m9zCkSjV.png

todo\lib\models\todo_model.dart

import 'dart:convert';

class TodoModel {
  TodoModel({
    required this.title,
    required this.done,
    required this.date,
  });

  final String title;
  final bool done;
  final String date;

  factory TodoModel.fromJson(String str) => TodoModel.fromMap(json.decode(str));

  String toJson() => json.encode(toMap());

  factory TodoModel.fromMap(Map<String, dynamic> json) => TodoModel(
        title: json["title"],
        done: json["done"],
        date: json["date"],
      );

  Map<String, dynamic> toMap() => {
        "title": title,
        "done": done,
        "date": date,
      };
}

創建我們的狀態管理

todo\lib\states\Itodo_state.dart

import 'package:todo/models/todo_model.dart';

abstract class ITodoState {
  List<TodoModel> getTodos();
  void addTodo(TodoModel todo);
}

todo\lib\states\todo_state.dart

import 'package:flutter/cupertino.dart';
import 'package:todo/models/todo_model.dart';
import 'package:todo/states/Itodo_state.dart';

class TodoState with ChangeNotifier implements ITodoState {
  List<TodoModel> todos = [];

  @override
  List<TodoModel> getTodos() {
    // TODO: implement getTodos
    notifyListeners();
    return todos;
  }

  @override
  void addTodo(TodoModel todo) {
    // TODO: implement addTodo
    todos.add(todo);
    notifyListeners();
  }
}

在app入口初始化

我們在app入口加入我們的state
main.dart

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todo/states/todo_state.dart';
import 'package:todo/ui/screens/nav_screen.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider.value(
      value: TodoState(),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: NavScreen(),
        debugShowCheckedModeBanner: false,
      ),
    );
  }
}

使用共享狀態資料

創建todo\lib\ui\widgets\todo_list_widget.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:provider/provider.dart';
import 'package:todo/states/todo_state.dart';
import 'package:todo/ui/widgets/todo_item_widget.dart';

class TodoList extends StatelessWidget {
  const TodoList({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final _todoState = Provider.of<TodoState>(context);

    return Column(
      children: _todoState.todos
          .map(
            (e) => TodoItem(title: e.title, done: e.done),
          )
          .toList(),
    );
  }
}

接著修改
todo\lib\ui\widgets\todo_item_widget.dart

import 'dart:math';

import 'package:flutter/material.dart';

class TodoItem extends StatefulWidget {
  final String title;
  final bool done;

  const TodoItem({
    Key? key,
    required this.title,
    required this.done,
  }) : super(key: key);
  @override
  State<TodoItem> createState() => _TodoItemState();
}

class _TodoItemState extends State<TodoItem> {
  late Color color = Color(Random().nextInt(0xffffffff));

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const FlutterLogo(),
          Text(widget.title),
          TextButton(onPressed: () {}, child: Text('Done')),
          ElevatedButton(onPressed: () {}, child: Text('Delete')),
        ],
      ),
    );
  }
}

以及將TodoList加入到我們的screen中

todo\lib\ui\screens\home_screen.dart

import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:provider/provider.dart';
import 'package:todo/models/todo_model.dart';
import 'package:todo/states/todo_state.dart';
import 'package:todo/ui/widgets/todo_item_widget.dart';
import 'package:todo/ui/widgets/todo_list_widget.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final TrackingScrollController _trackingScrollController =
      TrackingScrollController();

  @override
  Widget build(BuildContext context) {
    final _todoState = Provider.of<TodoState>(context);

    TodoModel testTodo = TodoModel(
        title: "Just Test", done: false, date: DateTime.now().toString());

    return CustomScrollView(
      controller: _trackingScrollController,
      slivers: [
        SliverAppBar(
          title: Text('Home Screen'),
        ),
        SliverToBoxAdapter(
          child: TodoList(),
        ),
        SliverToBoxAdapter(
          child: ElevatedButton(
            child: Text('Add todo test'),
            onPressed: () {
              _todoState.addTodo(testTodo);
            },
          ),
        ),
      ],
    );
  }
}

測試Provider

接著,我們來測試。我們能看到我們能跨組建的進行狀態管理了。

簡單來說,就是我們在根widget引入了Provider,而在其之下的子widgets裡的狀態改變後會往上傳回Provider,之後在往下面傳遞到我們的子widget中。

app-test.gif

我們簡單的介紹如何使用Provider後,明天我們會使用我們主要使用的狀態管理Riverpod來進行我們Todo管理,我們明天見


上一篇
15.flutter的狀態管理(三)
下一篇
17.flutter的狀態管理(五)
系列文
30天學習flutter30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言