Flutter状态管理插件statescope的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter状态管理插件statescope的使用

StateScope

StateScopeSimple State Management 的一个分支。它利用了 InheritedNotifier 来实现非常简单的状态管理。

特点

  • 极其容易使用。
  • 轻量且性能良好。
  • 惰性创建状态。

使用方法

首先,你需要将状态存储在一个扩展了 ChangeNotifier 的类中:

class Counter extends ChangeNotifier {
  int count = 0;

  void countUp() {
    count++;
    notifyListeners(); // 通知所有监听者状态已更改
  }
}

然后,将这个类放入 StateScopecreator 中:

StateScope(
  creator: () => Counter(), // 创建一个新的Counter实例
  child: Builder(
    builder: (context) {
      // 使用watch()来监听状态变化并重建widget
      final counter = context.watch<Counter>();
      return Column(
        children: [
          Text('${counter.count}'), // 显示当前计数
          ElevatedButton(
            onPressed: () {
              // 使用read()来获取状态但不重建widget
              context.read<Counter>().countUp();
            },
            child: Text('Count up'),
          ),
        ],
      );
    },
  ),
);

如果希望立即创建状态而不是惰性创建,可以设置 lazy 参数为 false

StateScope(
  lazy: false,
  creator: () => Counter(),
  child: Builder(
    builder: (context) {
      final counter = context.watch<Counter>();
      return Column(
        children: [
          Text('${counter.count}'),
          ElevatedButton(
            onPressed: () {
              context.read<Counter>().countUp();
            },
            child: Text('Count up'),
          ),
        ],
      );
    },
  ),
);

完整示例

以下是一个完整的示例,展示了如何使用 StateScope 管理复杂的用户认证和计数器状态。

import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:statescope/statescope.dart';

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

class App extends StatelessWidget {
  const App({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Authed Counter',
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      themeMode: ThemeMode.system,
      debugShowCheckedModeBanner: false,
      home: StateScope(
        creator: () => AuthState(),
        child: Builder(builder: (context) {
          final authState = context.watch<AuthState>();
          if (authState.isLoggedIn) {
            return StateScope(
              creator: () => AppState(),
              child: const HomePage(title: 'Authed Counter'),
            );
          } else {
            return const LoginPage();
          }
        }),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key, required this.title});

  final String title;

  [@override](/user/override)
  Widget build(BuildContext context) {
    final appState = context.watch<AppState>();
    return Scaffold(
      appBar: PreferredSize(
        preferredSize: const Size.fromHeight(kToolbarHeight),
        child: AppBar(
          title: Text(title, style: TextStyle(color: appState.textColor)),
          backgroundColor: appState.backgroundColor,
          actions: [
            ElevatedButton(
              onPressed: () {
                context.read<AuthState>().logout();
              },
              child: const Text('Logout'),
            ),
          ],
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('You have pushed the button this many times:'),
            Text(appState.count.toString()),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          context.read<AppState>().incrementCounter();
        },
        tooltip: 'Increment',
        backgroundColor: appState.backgroundColor,
        child: Icon(Icons.add, color: appState.textColor),
      ),
    );
  }
}

class LoginPage extends StatelessWidget {
  const LoginPage({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    final authState = context.watch<AuthState>();
    return Scaffold(
      body: Center(
        child: authState.isLogging
            ? const CircularProgressIndicator()
            : Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  OutlinedButton(
                    onPressed: () {
                      context.read<AuthState>().login();
                    },
                    child: const Text('Login'),
                  ),
                  if (authState.error != null)
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Text(
                        authState.error!,
                        style: const TextStyle(color: Colors.red),
                      ),
                    ),
                ],
              ),
      ),
    );
  }
}

enum _AuthState { loggedOut, logging, loggedIn }

class AuthState extends ChangeNotifier {
  var _authState = _AuthState.loggedOut;
  String? error;

  bool get isLogging => _authState == _AuthState.logging;
  bool get isLoggedIn => _authState == _AuthState.loggedIn;

  void login() {
    unawaited(_login());
  }

  Future<void> _login() async {
    error = null;
    _authState = _AuthState.logging;
    notifyListeners();

    try {
      await Future.delayed(const Duration(milliseconds: 500));
      final rand = Random().nextDouble();
      if (rand > 0.9) {
        throw Exception("intentional random error example");
      }
    } catch (e) {
      error = 'Login failed: ${e.toString()}';
      logout();
      return;
    }

    _authState = _AuthState.loggedIn;
    notifyListeners();
  }

  void logout() {
    _authState = _AuthState.loggedOut;
    notifyListeners();
  }
}

const _colors = [
  Colors.blue,
  Colors.red,
  Colors.green,
  Colors.purple,
  Colors.yellow,
];

class AppState extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void incrementCounter() {
    _count++;
    _backgroundColor = _colors[count % 5];
    notifyListeners();
  }

  Color _backgroundColor = Colors.blue;
  Color get backgroundColor => _backgroundColor;

  Color get textColor =>
      _backgroundColor == Colors.yellow ? Colors.black87 : Colors.white;
}

更多关于Flutter状态管理插件statescope的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter状态管理插件statescope的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter开发中,状态管理是一个核心问题,特别是在构建复杂应用时。StateScope 是一个相对较新的状态管理库,它提供了一种简洁而强大的方式来管理 Flutter 应用中的状态。以下是一个简单的示例,展示了如何在 Flutter 项目中使用 StateScope 进行状态管理。

首先,确保你的 Flutter 项目中已经添加了 state_scope 依赖。在你的 pubspec.yaml 文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  state_scope: ^最新版本号  # 请替换为实际的最新版本号

然后,运行 flutter pub get 来安装依赖。

接下来,我们将展示一个简单的示例,其中包含一个计数器应用,使用 StateScope 来管理计数器状态。

1. 创建主应用组件

import 'package:flutter/material.dart';
import 'package:state_scope/state_scope.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ScopeHost(
        scope: CounterScope(),
        child: CounterPage(),
      ),
    );
  }
}

2. 定义状态作用域

创建一个新的 Dart 文件 counter_scope.dart 来定义 CounterScope

import 'package:state_scope/state_scope.dart';

class CounterScope extends Scope {
  var _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notify();
  }
}

3. 创建显示计数器的页面

import 'package:flutter/material.dart';
import 'package:state_scope/state_scope.dart';
import 'counter_scope.dart';

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterScope = useScope<CounterScope>();

    return Scaffold(
      appBar: AppBar(
        title: Text('Counter App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${counterScope.count}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counterScope.increment();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

解释

  1. MyApp:主应用组件,使用 ScopeHost 包装 CounterScope 并将其传递给 CounterPage

  2. CounterScope:自定义的状态作用域,包含一个整数 _count 和一个 increment 方法来更新计数并通知监听器。

  3. CounterPage:使用 useScope<CounterScope>()ScopeHost 获取 CounterScope 实例,并显示当前的计数。同时,使用一个浮动操作按钮来调用 increment 方法增加计数。

通过这种方式,你可以使用 StateScope 在 Flutter 应用中有效地管理状态。StateScope 提供了简洁的 API 和高效的状态更新机制,非常适合用于构建复杂且响应迅速的 Flutter 应用。

回到顶部