📜  Flutter – 状态管理中 setState 和 Provider 的区别

📅  最后修改于: 2021-09-12 10:48:38             🧑  作者: Mango

在本文中,我们将研究如何使用状态管理或提供程序包在我们的flutter应用程序中实现状态管理。

我们正在flutter中创建一个简单的计数器应用程序,这将帮助我们理解flutter应用程序中的状态管理并决定哪种方法是构建我们的应用程序和完成项目的更好方法。

我们将讨论理解flutter应用程序中状态管理所需的两个最重要的方面,称为setState和名为 provider 的包。

入门

我们将从默认情况下可用的基本flutter结构开始。创建一个名为Home Page的无状态小部件,并将其分配给我们的 Material App 的 home 属性。

Dart
import 'package:flutter/material.dart';
  
void main() => runApp(MyApp());
   
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Material App',
        home: HomePage()
      );
  }
}


Dart
class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}
  
class _HomePageState extends State {
  int itemCount = 0 ;
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.forward),
        onPressed: (){
          Navigator.push(context, MaterialPageRoute(builder: (_){
            return SecondPage(
              count: itemCount,
            );
          }));
        },
      ),
      body: Column(
        children: [
          Container(
            alignment: Alignment.center,
            height: 300.0,
            child: Text('$itemCount',style: TextStyle(fontSize: 30.0)),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(onPressed: (){
                setState(() {
                  itemCount++ ; 
                });
              }, child: Text('Add')),
              ElevatedButton(onPressed: (){
                setState(() {
                  itemCount-- ; 
                });
              }, child: Text('Delete'))
            ],
          )
        ],
      ),
    );
  }
}


Dart
class SecondPage extends StatefulWidget {
  int count ;
  SecondPage({this.count}) ;
  
  @override
  _SecondPageState createState() => _SecondPageState();
}
  
class _SecondPageState extends State {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          Container(
            alignment: Alignment.center,
            height: 300.0,
            child: Text('${widget.count}',style: TextStyle(fontSize: 30.0)),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(onPressed: (){
                setState(() {
                  widget.count++ ;
                });
              }, child: Text('Add')),
              ElevatedButton(onPressed: (){
                setState(() {
                  widget.count-- ;
                });
              }, child: Text('Delete'))
            ],
          )
        ],
      ),
    );
  }
}


Dart
import 'package:flutter/material.dart';
  
class Manage extends ChangeNotifier{
    int count = 0 ;
  
    int get counter{
      return count ; 
    }
  
    void increaseCounter(){
      count++ ;
      notifyListeners();
    }
  
     void decreaseCounter(){
        count-- ;
        notifyListeners();
     }
}


Dart
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(value: Manage())
      ],
          child: MaterialApp(
        home: HomePage(),
      ),
    );
  }
}


Dart
class HomePage extends StatelessWidget {
     
  @override
  Widget build(BuildContext context) {
    int itemCount = Provider.of(context).counter;
  
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.forward),
        onPressed: (){
          Navigator.push(context, MaterialPageRoute(builder: (_){
            return SecondPage();
          }));
        },
      ),
      body: Column(
        children: [
          Container(
            alignment: Alignment.center,
            height: 300.0,
            child: Text('$itemCount',style: TextStyle(fontSize: 30.0)),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(onPressed: (){
                Provider.of(context,listen:false).increaseCounter();
              }, child: Text('Add')),
              ElevatedButton(onPressed: (){
                Provider.of(context,listen:false).decreaseCounter();
              }, child: Text('Delete'))
            ],
          )
        ],
      ),
    );
  }
}


Dart
class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
   int itemCount  = Provider.of(context).counter ; 
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          Container(
            alignment: Alignment.center,
            height: 300.0,
            child: Text('$itemCount',style: TextStyle(fontSize: 30.0)),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(onPressed: (){
                Provider.of(context,listen: false).increaseCounter();
              }, child: Text('Add')),
              ElevatedButton(onPressed: (){
                Provider.of(context,listen: false).decreaseCounter();
              }, child: Text('Delete'))
            ],
          )
        ],
      ),
    );
  }
}


我们的应用程序结构

在“主页”屏幕内,我们将使其成为一个有状态的小部件,它将由两个按钮组成,这些按钮具有使用设置状态功能增加和减少计数器的功能。还有一个浮动操作按钮,它将导航我们到我们应用程序的第二个屏幕,在那里将再次跟踪我们的变量itemCount

Dart

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}
  
class _HomePageState extends State {
  int itemCount = 0 ;
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.forward),
        onPressed: (){
          Navigator.push(context, MaterialPageRoute(builder: (_){
            return SecondPage(
              count: itemCount,
            );
          }));
        },
      ),
      body: Column(
        children: [
          Container(
            alignment: Alignment.center,
            height: 300.0,
            child: Text('$itemCount',style: TextStyle(fontSize: 30.0)),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(onPressed: (){
                setState(() {
                  itemCount++ ; 
                });
              }, child: Text('Add')),
              ElevatedButton(onPressed: (){
                setState(() {
                  itemCount-- ; 
                });
              }, child: Text('Delete'))
            ],
          )
        ],
      ),
    );
  }
}

在第二个屏幕内,我们将再次拥有用于增加和减少计数器的按钮。借助 set State 功能

这将是一个有状态的小部件。

Dart

class SecondPage extends StatefulWidget {
  int count ;
  SecondPage({this.count}) ;
  
  @override
  _SecondPageState createState() => _SecondPageState();
}
  
class _SecondPageState extends State {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          Container(
            alignment: Alignment.center,
            height: 300.0,
            child: Text('${widget.count}',style: TextStyle(fontSize: 30.0)),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(onPressed: (){
                setState(() {
                  widget.count++ ;
                });
              }, child: Text('Add')),
              ElevatedButton(onPressed: (){
                setState(() {
                  widget.count-- ;
                });
              }, child: Text('Delete'))
            ],
          )
        ],
      ),
    );
  }
}

成功运行我们的代码后,我们可以看到我们没有得到我们想要的结果。计数器变量没有得到正确的值。

输出

因此,为了克服这个问题并在我们的应用程序中提供更好的功能,我们将使用 provider 包。当我们必须处理数据动态变化的多屏应用程序时,它非常有用,提供程序为我们提供了一个更好的工具,使我们的数据随着我们应用程序的每个函数而更新。

在我们flutter应用首次进口提供商的依赖内部pubspec.yam l文件。

现在在另一个文件中创建一个类,将这个类扩展为 Change Notifier。这个类将包含增加和减少计数器变量的功能。不要忘记在我们的函数包含通知监听器,否则它将无法正常工作。此外,创建一个将返回计数变量的 getter。

Dart

import 'package:flutter/material.dart';
  
class Manage extends ChangeNotifier{
    int count = 0 ;
  
    int get counter{
      return count ; 
    }
  
    void increaseCounter(){
      count++ ;
      notifyListeners();
    }
  
     void decreaseCounter(){
        count-- ;
        notifyListeners();
     }
}

为了让我们的应用程序知道并了解提供者,我们必须在 main.js 中向我们的 Material 应用程序添加一些东西。dart。包装我们的材料应用程序。在 MultiProvider 功能内部,并将我们的类添加到 Multi Provider 的 providers 选项中。

Dart

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(value: Manage())
      ],
          child: MaterialApp(
        home: HomePage(),
      ),
    );
  }
}

现在剩下的也是最后一个任务是更新我们主页屏幕中的某些内容,以便它可以收听我们的提供者。

首先,使主页成为无状态小部件。

Dart

class HomePage extends StatelessWidget {
     
  @override
  Widget build(BuildContext context) {
    int itemCount = Provider.of(context).counter;
  
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.forward),
        onPressed: (){
          Navigator.push(context, MaterialPageRoute(builder: (_){
            return SecondPage();
          }));
        },
      ),
      body: Column(
        children: [
          Container(
            alignment: Alignment.center,
            height: 300.0,
            child: Text('$itemCount',style: TextStyle(fontSize: 30.0)),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(onPressed: (){
                Provider.of(context,listen:false).increaseCounter();
              }, child: Text('Add')),
              ElevatedButton(onPressed: (){
                Provider.of(context,listen:false).decreaseCounter();
              }, child: Text('Delete'))
            ],
          )
        ],
      ),
    );
  }
}

确保在用于调用函数的提供程序中使 listen 参数的值为false 。仅当我们必须在应用程序屏幕中使用该值或收听该值时,我们才会使其为真。 (比如itemCount的值)

同样,我们可以相应地更新我们的 Second Page 小部件。将其转换为无状态小部件

Dart

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
   int itemCount  = Provider.of(context).counter ; 
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          Container(
            alignment: Alignment.center,
            height: 300.0,
            child: Text('$itemCount',style: TextStyle(fontSize: 30.0)),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(onPressed: (){
                Provider.of(context,listen: false).increaseCounter();
              }, child: Text('Add')),
              ElevatedButton(onPressed: (){
                Provider.of(context,listen: false).decreaseCounter();
              }, child: Text('Delete'))
            ],
          )
        ],
      ),
    );
  }
}

输出

因此我们可以看到 provider 很好地完成了我们的工作并且易于使用。