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
可以通过 putValue
和 notifyFor
方法来更新全局状态并重建视图:
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