Flutter Provider状态管理---八种提供者使用分析

Provider

Provider是最基本的Provider组件,可以使用它为组件树中的任何位置提供值,但是当该值更改的时候,它并不会更新UI

class UserModel {

  String name = "kwok";

  void changeName() {
    name = "hello";
  }
}

return Provider(
  create: (_) => UserModel(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ProviderExample(),
  ),
);

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/provider_example/user_model.dart';
import 'package:provider/provider.dart';

class ProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderExample"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer(
              builder: (_, userModel, child) {
                return Text(userModel.name,
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer(
              builder: (_, userModel, child) {
                return Padding(
                  padding: EdgeInsets.all(20),
                  child: ElevatedButton(
                    onPressed: (){
                      userModel.changeName();
                    },
                    child: Text("改变值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

运行结果

我们点击按钮的会导致模型数据改变,但是模型数据改变之后UI并没有变化也没有重建,那是因为Provider提供者组件不会监听它提供的值的变化。

ChangeNotifierProvider

它跟Provider组件不同,ChangeNotifierProvider会监听模型对象的变化,而且当数据改变时,它也会重建Consumer(消费者),下面我们给出一个示例

import 'package:flutter/material.dart';

class UserModel1 with ChangeNotifier {

  String name = "Kwok";

  void changeName() {
    name = "hello";
    notifyListeners();
  }
}
return ChangeNotifierProvider(
  create: (_) => UserModel1(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ChangeNotifierProviderExample(),
  ),
);
import 'package:flutter/material.dart';
import 'package:flutter_provider_example/change_notifier_provider_example/user_model1.dart';
import 'package:provider/provider.dart';

class ChangeNotifierProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ChangeNotifierProvider"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer(
              builder: (_, userModel, child) {
                return Text(userModel.name,
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer(
              builder: (_, userModel, child) {
                return Padding(
                  padding: EdgeInsets.all(20),
                  child: ElevatedButton(
                    onPressed: (){
                      userModel.changeName();
                    },
                    child: Text("改变值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

FutureProvider

简单来说,FutureProvider用于提供在组件树中准备好使用其值时可能尚未准备好的值,主要是确保空值不会传递给任何子组件,而且FutureProvider有一个初始值,子组件可以使用该Future值并告诉子组件使用新的值来进行重建。

注意:

  • FutureProvider只会重建一次
  • 默认显示初始值
  • 然后显示Future
  • 最后不会再次重建
    第一步:创建模型
    这里和Provider不同的是增加了构造函数,以及changeName变成了Future,我们模拟网络请求延迟两秒后改变其值。
    
    class UserModel2{
    
      UserModel2({this.name});
    
      String? name = "Jimi";
    
      Future changeName() async {
        await Future.delayed(Duration(milliseconds: 2000));
        name = "hello";
      }
    }
    第二步:提供Future
    我们有一个方法,就是异步获取userModel2,模拟网络请求延迟两秒执行,最后修改了name并返回UserModel2
    
    import 'package:flutter_provider_example/future_provider_example/user_model2.dart';
    
    class UserFuture {
    
      Future asyncGetUserModel2() async {
        await Future.delayed(Duration(milliseconds: 2000));
        return UserModel2(name: "获取新的数据");
      }
    
    }
    第三步:应用程序入口设置
    initialData是默认值,create参数我们传了一个Future,因为它接收的模型Create?>
    
    return FutureProvider(
      initialData: UserModel2(name: "hello"),
      create: (_) => UserFuture().asyncGetUserModel2(),
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        home: FutureProviderExample(),
      ),
    );
    第四步:使用共享数据
    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/future_provider_example/user_model2.dart';
    import 'package:provider/provider.dart';
    
    class FutureProviderExample extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("FutureProviderExample"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Consumer(
                  builder: (_, userModel, child) {
                    return Text(userModel.name ?? "",
                        style: TextStyle(
                            color: Colors.red,
                            fontSize: 30
                        )
                    );
                  },
                ),
                Consumer(
                  builder: (_, userModel, child) {
                    return Padding(
                      padding: EdgeInsets.all(20),
                      child: ElevatedButton(
                        onPressed: (){
                          userModel.changeName();
                        },
                        child: Text("改变值"),
                      ),
                    );
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    

    StreamProvider

    StreamProvider提供流值,是围绕StreamBuilder,所提供的值会在传入的时候替换掉新值。和FutureProvider一样,主要的区别在于值会根据多次触发重新构建UI。

    第一步:创建模型

    class UserModel3{
    
      UserModel3({this.name});
    
      String? name = "kwok";
    
      void changeName() {
        name = "hello";
      }
    }
    

        第二步:提供Stream

        下面这段代码类似计时器,每隔一秒钟生成一个数字

import 'package:flutter_provider_example/stream_provider_example/user_model3.dart';


class UserStream {

  Stream getStreamUserModel() {
    return Stream.periodic(Duration(milliseconds: 1000),
        (value) => UserModel3(name: "$value")
    ).take(10);
  }
}

  • 第三步:应用程序入口设置

    这里也有initialData初始值,和FutureProvider类似,只是create属性是获取一个Stream流。

     
    return StreamProvider(
      initialData: UserModel3(name: "hello"),
      create: (_) => UserStream().getStreamUserModel(),
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        home: StreamProviderExample(),
      ),
    );

    第四步:使用共享数据

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/stream_provider_example/user_model3.dart';
    import 'package:provider/provider.dart';
    
    class StreamProviderExample extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("StreamProviderExample"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Consumer(
                  builder: (_, userModel, child) {
                    return Text(userModel.name ?? "",
                        style: TextStyle(
                            color: Colors.red,
                            fontSize: 30
                        )
                    );
                  },
                ),
                Consumer(
                  builder: (_, userModel, child) {
                    return Padding(
                      padding: EdgeInsets.all(20),
                      child: ElevatedButton(
                        onPressed: (){
                          userModel.changeName();
                        },
                        child: Text("改变值"),
                      ),
                    );
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    

  • MultiProvider

    在上面的例子中我们都只是返回了一个提供者,在实际开发过程中肯定会有多个提供者,我们虽然可以采用嵌套的方式来解决,但是这样无疑是混乱的,可读性级差。这个时候强大的MultiProvder就产生了,我们来看下示例:

    第一步:创建两个模型

    import 'package:flutter/material.dart';
    
    class UserModel1 with ChangeNotifier {
    
      String name = "kwok";
    
      void changeName() {
        name = "hello";
        notifyListeners();
      }
    }
    
    class UserModel4 with ChangeNotifier {
    
      String name = "kwok";
      int age = 18;
    
      void changeName() {
        name = "hello";
        age = 20;
        notifyListeners();
      }
    }

  • 第二步:应用程序入口设置

    相对于方式一这种嵌套方式设置,方式二就显得尤为简单。

    方式一:嵌套设置

    return ChangeNotifierProvider(
      create: (_) => UserModel1(),
      child: ChangeNotifierProvider(
        create: (_) => UserModel4(),
        child: MaterialApp(
          debugShowCheckedModeBanner: false,
          home: MultiProviderExample(),
        ),
      ),
    );

  • 方式二:使用MultiProvider

    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => UserModel1()),
        ChangeNotifierProvider(create: (_) => UserModel4()),
        /// 添加更多
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        home: MultiProviderExample(),
      ),
    );
    

    第三步:使用共享数据

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/change_notifier_provider_example/user_model1.dart';
    import 'package:flutter_provider_example/multi_provider_example/user_model4.dart';
    import 'package:provider/provider.dart';
    
    class MultiProviderExample extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("MultiProviderExample"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Consumer(
                  builder: (_, userModel, child) {
                    return Text(userModel.name,
                        style: TextStyle(
                            color: Colors.red,
                            fontSize: 30
                        )
                    );
                  },
                ),
                Consumer(
                  builder: (_, userModel, child) {
                    return Text(userModel.age.toString(),
                        style: TextStyle(
                            color: Colors.green,
                            fontSize: 30
                        )
                    );
                  },
                ),
                Consumer2(
                  builder: (_, userModel1, userModel4, child) {
                    return Padding(
                      padding: EdgeInsets.all(20),
                      child: ElevatedButton(
                        onPressed: (){
                          userModel1.changeName();
                          userModel4.changeName();
                        },
                        child: Text("改变值"),
                      ),
                    );
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    

    ProxyProvider

    当我们有多个模型的时候,会有模型依赖另一个模型的情况,在这种情况下,我们可以使用ProxyProvider从另一个提供者获取值,然后将其注入到另一个提供者中。我们来看下代码演示

    第一步:创建两个模型

    下面我们创建了两个模型UserModel5WalletModel,而WalletModel依赖与UserModel5,当调用WalletModelchangeName方法时会改变UserModel5里面的name,当然我们在实际开发的过程中并不是这么简单,这里只是演示模型依赖时如果使用ProxyProvider

    import 'package:flutter/material.dart';
    
    class UserModel5 with ChangeNotifier {
    
      String name = "keo";
    
      void changeName({required String newName}) {
        name = newName;
        notifyListeners();
      }
    }
    
    
    class WalletModel {
    
      UserModel5? userModel5;
    
      WalletModel({this.userModel5});
    
      void changeName() {
        userModel5?.changeName(newName: "JIMI");
      }
    }
    

    第二步:应用程序入口设置

    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => UserModel5()),
        ProxyProvider(
          update: (_, userModel5, walletModel) => WalletModel(userModel5: userModel5),
        )
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        home: ProxyProviderExample(),
      ),
    );
    

    第三步:使用共享数据

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/proxy_provider_example/user_model5.dart';
    import 'package:flutter_provider_example/proxy_provider_example/wallet_model.dart';
    import 'package:provider/provider.dart';
    
    class ProxyProviderExample extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("ProxyProviderExample"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Consumer(
                  builder: (_, userModel, child) {
                    return Text(userModel.name,
                        style: TextStyle(
                            color: Colors.red,
                            fontSize: 30
                        )
                    );
                  },
                ),
                Consumer(
                  builder: (_, userModel, child) {
                    return Padding(
                      padding: EdgeInsets.all(20),
                      child: ElevatedButton(
                        onPressed: (){
                          userModel.changeName(newName: "hello");
                        },
                        child: Text("改变值"),
                      ),
                    );
                  },
                ),
                Consumer(
                  builder: (_, walletModel, child) {
                    return Padding(
                      padding: EdgeInsets.all(20),
                      child: ElevatedButton(
                        onPressed: (){
                          walletModel.changeName();
                        },
                        child: Text("通过代理改变值"),
                      ),
                    );
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    

    ChangeNotifierProxyProvider

  • ChangeNotifierProxyProvider和ProxyProvider原理一样,唯一的区别在于它构建和同步ChangeNotifierChangeNotifierProvider,当提供者数据变化时,将会重构UI。

    下面我们给出一个例子:

  • 获取书籍列表
  • 获取收藏书籍列表
  • 点击书籍可加入或者取消收藏
  • 通过代理实时重构UI
  • 第一步:创建两个模型

    1、BookModel

    BookModel用户存储模型数据,将书籍转换成模型。

    class BookModel {
      
      static var _books = [
        Book(1, "夜的命名数"),
        Book(2, "大奉打更人"),
        Book(3, "星门"),
        Book(4, "大魏读书人"),
        Book(5, "我师兄实在太稳健了"),
        Book(6, "深空彼岸"),
      ];
    
      // 获取书籍长度
      int get length => _books.length;
    
      // 根据ID获取书籍
      Book getById(int id) => _books[id -1];
    
      // 根据索引获取数据
      Book getByPosition(int position) => _books[position];
    
      // 更多....
    }
    
    class Book {
      final int bookId;
      final String bookName;
      
      Book(this.bookId, this.bookName);
    }
    

    2、BookManagerModel

    BookManagerModel主要用于管理书籍、收藏书籍、取消收藏等操作

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';
    
    class BookManagerModel with ChangeNotifier {
    
      // 依赖bookModel
      final BookModel _bookModel;
    
      // 获取数据所有的ID
      List? _bookIds;
    
      // 构造函数
      BookManagerModel(this._bookModel, {BookManagerModel? bookManagerModel})
        : _bookIds = bookManagerModel?._bookIds ?? [];
    
      // 获取所有的书
      List get books => _bookIds!.map((id) => _bookModel.getById(id)).toList();
    
      // 根据索引获取数据
      Book getByPosition(int position) => books[position];
    
      // 获取书籍的长度
      int get length => _bookIds?.length ?? 0;
    
      // 添加书籍
      void addFaves(Book book) {
        _bookIds!.add(book.bookId);
        notifyListeners();
      }
    
      // 删除书籍
      void removeFaves(Book book) {
        _bookIds!.remove(book.bookId);
        notifyListeners();
      }
    }

    第二步:应用程序入口设置

    return MultiProvider(
      providers: [
        Provider(create: (_) => BookModel()),
        ChangeNotifierProxyProvider(
          create: (_) => BookManagerModel(BookModel()),
          update: (_, bookModel, bookManagerModel) => BookManagerModel(bookModel),
        )
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        home: ChangeNotifierProxyProviderExample(),
      ),
    );

    第三步:设置BottomNavigationBar

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/pages/page_a.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/pages/page_b.dart';
    
    class ChangeNotifierProxyProviderExample extends StatefulWidget {
      @override
      _ChangeNotifierProxyProviderExampleState createState() => _ChangeNotifierProxyProviderExampleState();
    }
    
    class _ChangeNotifierProxyProviderExampleState extends State {
    
    
      var _selectedIndex = 0;
      var _pages = [PageA(), PageB()];
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: _pages[_selectedIndex],
          bottomNavigationBar: BottomNavigationBar(
            currentIndex: _selectedIndex,
            onTap: (index) {
              setState(() {
                _selectedIndex = index;
              });
            },
            items: [
              BottomNavigationBarItem(
                icon: Icon(Icons.book),
                label: "书籍列表"
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.favorite),
                label: "收藏"
              )
            ],
          ),
        );
      }
    }
    
    

    第四步:书籍列表UI构建

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_item.dart';
    import 'package:provider/provider.dart';
    
    class PageA extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        
        var bookModel = Provider.of(context);
        
        return Scaffold(
          appBar: AppBar(
            title: Text("书籍列表"),
          ),
          body: ListView.builder(
            itemCount: bookModel.length,
            itemBuilder: (_, index) => BookItem(id: index + 1),
          ),
        );
      }
    }

  • 第五步:收藏列表UI构建

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_item.dart';
    import 'package:provider/provider.dart';
    
    class PageB extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
    
        var bookManagerModel = Provider.of(context);
        var bookCount = bookManagerModel.length;
    
        return Scaffold(
          appBar: AppBar(
            title: Text("收藏列表"),
          ),
          body: ListView.builder(
            itemCount: bookCount,
            itemBuilder: (_, index) => BookItem(id: bookManagerModel.getByPosition(index).bookId),
          ),
        );
      }
    }
    

    其他辅助封装类

    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';
    import 'package:provider/provider.dart';
    
    class BookButton extends StatelessWidget {
      
      final Book book;
      
      BookButton({
        Key? key,
        required this.book
      }) : super(key: key);
      
      @override
      Widget build(BuildContext context) {
        
        var bookManagerModel = Provider.of(context);
        
        return GestureDetector(
          onTap: bookManagerModel.books.contains(this.book)
              ?  () => bookManagerModel.removeFaves(this.book)
              :  () => bookManagerModel.addFaves(this.book),
          child: SizedBox(
            width: 100,
            height: 60,
            child: bookManagerModel.books.contains(this.book)
                ?  Icon(Icons.star, color: Colors.red,)
                :  Icon(Icons.star_border),
          ),
        );
      }
    }
    import 'package:flutter/material.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';
    import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_button.dart';
    import 'package:provider/provider.dart';
    
    class BookItem extends StatelessWidget {
    
      final int id;
    
      BookItem({
        Key? key,
        required this.id
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
    
        var bookModel = Provider.of(context);
        var book = bookModel.getById(id);
    
        return ListTile(
          leading: CircleAvatar(
            child: Text("${book.bookId}"),
          ),
          title: Text("${book.bookName}",
            style: TextStyle(
                color: Colors.black87
            ),
          ),
          trailing: BookButton(book: book),
        );
      }
    }

    总结

    Provider为我们提供了非常多的提供者,总共有八种。但我们比较常用的是ChangeNotifierProviderMultiProviderChangeNotifierProxyProvider,关于其他的提供者可根据自己的实际应用场景来。

你可能感兴趣的:(Flutter,flutter)