Flutter自定义小工具集合插件flutter_gadgets_2的使用

Flutter 自定义小工具集合插件 flutter_gadgets_2 的使用

简介

flutter_gadgets_2 是一个用于简化状态(模型)管理和服务定位的 Flutter 库。它遵循类似 Redux 的原则,但更容易采用。

示例 Demo

Counter 示例

以下是一个简单的计数器应用,展示了如何使用 flutter_gadgets_2 插件。

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

void main() => runApp(AppGadget(
      init: (model) => model.putValue<int>(value: 0),
      child: const MyApp(),
    ));

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Counter'),
        ),
        body: Center(
          child: SubscriberGadget<int>(
            builder: (_, value) => Text('Count: $value'),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => context.read<ObservableModel>().updateValue<int>(
                updateFunction: (value) => value + 1,
              ),
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

当应用程序启动时,AppGadget 注册一个新的 int 实例到 ObservableModel 中:现在该对象在整个应用中都可全局访问。

为了将计数器实例的值绑定到一个部件上,一个 Text 部件被包裹在一个 SubscriberGadget 中:这样它就能访问到 int 对象并在必要时更新。

更新模型实例和界面的任务委托给了 onPressed 回调:它会递增当前的 int 值,并触发相应订阅者的重建。

Rock Paper Scissor 示例

以下是经典的“石头剪刀布”游戏的一个例子,展示了如何在应用中管理业务逻辑。

class Turn {
  static final random = Random();

  late HandSign _playerChoice;
  late HandSign _cpuChoice;
  late TurnState state;

  TurnState play(HandSign playerChoice) {
    _playerChoice = playerChoice;
    _cpuChoice = _generateCpuChoice();
    state = _currentTurnState();
    return state;
  }

  HandSign _generateCpuChoice() {
    int index = random.nextInt(HandSign.values.length);
    return HandSign.values[index];
  }

  TurnState _currentTurnState() {
    if (_playerChoice == _cpuChoice) return TurnState.tie;
    if (_playerWon()) {
      return TurnState.playerWon;
    } else {
      return TurnState.cpuWon;
    }
  }

  bool _playerWon() {
    bool firstCase = _playerChoice == HandSign.paper && _cpuChoice == HandSign.rock;
    bool secondCase = _playerChoice == HandSign.rock && _cpuChoice == HandSign.scissor;
    bool thirdCase = _playerChoice == HandSign.scissor && _cpuChoice == HandSign.paper;
    return firstCase || secondCase || thirdCase;
  }

  HandSign get playerChoice => _playerChoice;

  HandSign get cpuChoice => _cpuChoice;
}

class Match {
  int cpuWins = 0;
  int playerWins = 0;
  int ties = 0;
  Turn? lastTurn;

  void nextTurn(HandSign playerChoice) {
    lastTurn = Turn();
    lastTurn!.play(playerChoice);
    _updateWins();
  }

  void _updateWins() {
    if (lastTurnState == TurnState.playerWon) {
      playerWins++;
    } else if (lastTurnState == TurnState.cpuWon) {
      cpuWins++;
    } else {
      ties++;
    }
  }

  MatchState get matchState {
    if (cpuWins == turnsToWin) return MatchState.cpuWon;
    if (playerWins == turnsToWin) return MatchState.playerWon;
    return MatchState.playing;
  }

  String get matchStateDescription {
    if (cpuWins == turnsToWin) return 'Cpu won!';
    if (playerWins == turnsToWin) return 'Player won!';
    return 'Playing...';
  }

  TurnState get lastTurnState => lastTurn!.state;

  String get lastTurnStateDescription {
    if (lastTurn == null) return 'Your turn.';
    if (lastTurnState == TurnState.playerWon) return 'You won this turn!';
    if (lastTurnState == TurnState.cpuWon) return 'Cpu won this turn!';
    return 'Tie!';
  }
}

当应用创建时,一个新的 Match 实例被添加到 ObservableModel 中:

AppGadget(
  init: (model) => model.putValue<Match>(value: Match()),
  child: const MaterialApp(
    title: 'Rock Paper Scissor',
    home: MyGameView(),
  ),
);

需要 Match 实例的部分视图被包裹在 SubscriberGadget 中,以确保其能够访问实例并根据匹配状态的变化重新构建屏幕。此外,为了访问与关联视图控制器 MyGameViewController 提供的回调,整个视图被包裹在 ViewGadget<MyGameViewController> 中。

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

  @override
  Widget build(BuildContext context) {
    return ViewGadget<MyGameViewController>(
      create: () => MyGameViewController(),
      builder: (_, control) => Scaffold(
        appBar: AppBar(
          title: const Text('Rock Paper Scissor'),
          centerTitle: false,
          actions: [
            IconButton(
              icon: const Icon(Icons.refresh),
              onPressed: control.onRefreshPressed,
            )
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: SubscriberGadget<Match>(
            builder: (_, match) => Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                const Text(
                  'Play a match!',
                  style: TextStyle(
                    fontWeight: FontWeight.w700,
                  ),
                ),
                Text(match.matchStateDescription),
                Text(match.lastTurnStateDescription),
                const SizedBox(height: 16),
                RowText('Player wins:', match.playerWins.toString()),
                RowText('Ties:', match.ties.toString()),
                RowText('Cpu wins:', match.cpuWins.toString()),
                const SizedBox(height: 16),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: [
                    TextButton(
                      'ROCK',
                      () => control.onButtonPressed(HandSign.rock),
                      match.matchState == MatchState.playing,
                    ),
                    TextButton(
                      'PAPER',
                      () => control.onButtonPressed(HandSign.paper),
                      match.matchState == MatchState.playing,
                    ),
                    TextButton(
                      'SCISSOR',
                      () => control.onButtonPressed(HandSign.scissor),
                      match.matchState == MatchState.playing,
                    ),
                  ],
                ),
                const SizedBox(height: 16),
                const HandSignRow()
              ],
            ),
          ),
        ),
      ),
    );
  }
}

MyGameViewController 可以通过 putValuenotifyFor 方法来更新全局状态并重建视图:

class MyGameViewController extends ControllerGadget {
  void onRefreshPressed() => model.putValue<Match>(value: Match());

  void onButtonPressed(HandSign playerChoice) {
    final match = model.getValue<Match>()!;
    match.nextTurn(playerChoice);
    model.notifyFor<Match>();
  }
}

更多关于Flutter自定义小工具集合插件flutter_gadgets_2的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自定义小工具集合插件flutter_gadgets_2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,flutter_gadgets_2 是一个假设的 Flutter 插件,用于集合各种自定义小工具。在实际场景中,具体的插件功能和 API 会有所不同,但我可以根据常见的 Flutter 插件开发模式,提供一个假设的示例代码,展示如何使用一个名为 flutter_gadgets_2 的插件。

首先,确保你已经在 pubspec.yaml 文件中添加了 flutter_gadgets_2 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_gadgets_2: ^x.y.z  # 替换为实际的版本号

然后,运行 flutter pub get 来获取依赖。

假设 flutter_gadgets_2 插件提供了几个自定义小工具,比如一个自定义的计数器小工具 CustomCounter 和一个自定义的加载指示器 CustomLoadingIndicator。以下是如何在你的 Flutter 应用中使用这些小工具的示例代码。

使用 CustomCounter 小工具

import 'package:flutter/material.dart';
import 'package:flutter_gadgets_2/flutter_gadgets_2.dart'; // 假设的导入路径

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Gadgets 2 Demo'),
        ),
        body: Center(
          child: CustomCounter(
            initialCount: 0,
            onIncrement: () {
              // 可以在这里处理计数器增加的逻辑
              print('Counter incremented');
            },
          ),
        ),
      ),
    );
  }
}

假设 CustomCounter 小工具的构造函数接受 initialCountonIncrement 回调。

使用 CustomLoadingIndicator 小工具

import 'package:flutter/material.dart';
import 'package:flutter_gadgets_2/flutter_gadgets_2.dart'; // 假设的导入路径

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Gadgets 2 Loading Indicator Demo'),
        ),
        body: Center(
          child: FutureBuilder<void>(
            future: Future.delayed(Duration(seconds: 3)), // 模拟异步操作
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return CustomLoadingIndicator(
                  color: Colors.blue,
                  size: 50.0,
                );
              } else if (snapshot.hasError) {
                return Text('Error: ${snapshot.error}');
              }
              return Text('Loading complete!');
            },
          ),
        ),
      ),
    );
  }
}

在这个例子中,CustomLoadingIndicator 小工具接受 colorsize 参数,用于自定义加载指示器的外观。我们使用 FutureBuilder 来模拟一个异步操作,并在等待期间显示加载指示器。

注意事项

  1. 实际插件文档:请务必查阅 flutter_gadgets_2 插件的实际文档,以获取准确的使用指南和 API 参考。
  2. 版本兼容性:确保你使用的 Flutter SDK 版本与 flutter_gadgets_2 插件兼容。
  3. 错误处理:在生产环境中使用时,添加适当的错误处理逻辑,以提高应用的健壮性。

由于 flutter_gadgets_2 是一个假设的插件,上述代码仅作为示例,实际使用时需要根据插件提供的 API 进行调整。

回到顶部