Flutter 多页表单体系结构
我正在尝试构建一个系统来创建具有多个页面的表单。我的方法是把它分成三个不同的部分Flutter 多页表单体系结构,flutter,Flutter,我正在尝试构建一个系统来创建具有多个页面的表单。我的方法是把它分成三个不同的部分 表单页面:不同的表单页面(每个页面都有自己的逻辑来验证字段) ProjectFormContainer:在导航器中保存页面的容器页面 MultiPageFormController:用于管理表单页面之间导航的控制器 我已经设法在项目FormContainer中添加了一个多页面控制器的ChangeNotifierProvider取得了一些进展,但我不确定如何将单个表单逻辑与其余元素连接起来,以及如何为该模型创建一个
这是一个基于的解决方案,作者是 我使用了以下软件包:
- 管理表单的流程
- 要摆脱有状态的小部件并拥有更精简的代码库
- 管理用户信息的不变性
- [可选]
完整的源代码,便于复制粘贴
import'package:flow_builder/flow_builder.dart';
进口“包装:颤振/材料.省道”;
进口“包装:颤振钩/颤振钩.省道”;
导入“package:freezed_annotation/freezed_annotation.dart”;
进口“包装:Flatter_colorpicker/Flatter_colorpicker.dart”;
零件“66228603.flow_builder.冻结.省道”;
void main(){
runApp(
材料聚丙烯(
debugShowCheckedModeBanner:false,
标题:“流程演示”,
主页:主页(),
),
);
}
//主页
类主页扩展了小部件{
@凌驾
小部件构建(构建上下文){
最终_userInfo=useState();
返回脚手架(
backgroundColor:_userInfo.value==null
?颜色:白色
:\u userInfo.value.favoriteColor,
appBar:appBar(标题:Text('Flow')),
主体:容器(
填充:边缘设置。全部(8.0),
对齐:对齐.center,
子项:_userInfo.value==null
?升降按钮(
onPressed:()异步{
_userInfo.value=
等待Navigator.of(context.push)(OnboardingFlow.route());
},
child:Text('GET start'),
)
:列(
mainAxisAlignment:mainAxisAlignment.center,
儿童:[
正文(
'欢迎,${u userInfo.value.name}!',
样式:TextStyle(fontSize:48.0),
),
const SizedBox(高度:48.0),
正文(
“那么,你已经${u userInfo.value.age}岁了,这是你最喜欢的颜色?太好了!”,
样式:TextStyle(fontSize:32.0),
),
],
),
),
);
}
}
//流动
类OnboardingFlow扩展了无状态小部件{
静态路由(){
返回物料管理路线(生成器:()=>OnboardingFlow());
}
@凌驾
小部件构建(构建上下文){
打印('INFO:${const UserInfo()}');
返回脚手架(
正文:FlowBuilder(
状态:const UserInfo(),
onGeneratePages:(个人资料,页数){
返回[
MaterialPage(子项:NameForm()),
如果(profile.name!=null)MaterialPage(子项:AgeForm()),
如果(profile.age!=null)MaterialPage(子项:ColorForm()),
];
},
),
);
}
}
//形式
类NameForm扩展了HookWidget{
@凌驾
小部件构建(构建上下文){
final _name=useState();
返回脚手架(
appBar:appBar(标题:const Text('Name')),
主体:容器(
填充:边缘设置。全部(8.0),
对齐:对齐.center,
子:列(
mainAxisAlignment:mainAxisAlignment.center,
儿童:[
文本字段(
自动对焦:对,
一旦更改:(值)=>\u name.value=value,
装饰:输入装饰(
labelText:'名称',
hintText:'输入您的姓名',
),
),
const SizedBox(高度:24.0),
升起的按钮(
子项:const Text('Continue'),
已按下:(){
if(_name.value.isNotEmpty){
上下文
.flow()
.update((info)=>info.copyWith(name:_name.value));
}
},
)
],
),
),
);
}
}
类AgeForm扩展了HookWidget{
@凌驾
小部件构建(构建上下文){
最终年龄=使用状态();
返回脚手架(
appBar:appBar(标题:const Text('Age')),
主体:容器(
填充:边缘设置。全部(8.0),
对齐:对齐.center,
子:列(
mainAxisAlignment:mainAxisAlignment.center,
儿童:[
下拉按钮窗体字段(
项目:List.generate(
200,
(索引)=>下拉菜单项(
值:索引,
子项:文本(index.toString()),
),
),
一旦更改:(值)=>\u age.value=value,
装饰:输入装饰(
标签文字:“年龄”,
hintText:“你多大了?”,
),
),
const SizedBox(高度:24.0),
升起的按钮(
子项:const Text('Continue'),
已按下:(){
如果(_age.value!=null){
FlowBuilder<UserInfo>(
state: const UserInfo(),
onGeneratePages: (profile, pages) {
return [
MaterialPage(child: NameForm()),
if (profile.name != null) MaterialPage(child: AgeForm()),
if (profile.age != null) MaterialPage(child: ColorForm()),
];
},
),
context
.flow<UserInfo>()
.update((info) => info.copyWith(name: _name.value));
context
.flow<UserInfo>()
.complete((info) => info.copyWith(favoriteColor: _color.value));
_userInfo.value = await Navigator.of(context).push(OnboardingFlow.route());
import 'package:flow_builder/flow_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
part '66228603.flow_builder.freezed.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flow Demo',
home: HomePage(),
),
);
}
// MAIN PAGE
class HomePage extends HookWidget {
@override
Widget build(BuildContext context) {
final _userInfo = useState<UserInfo>();
return Scaffold(
backgroundColor: _userInfo.value == null
? Colors.white
: _userInfo.value.favoriteColor,
appBar: AppBar(title: Text('Flow')),
body: Container(
padding: EdgeInsets.all(8.0),
alignment: Alignment.center,
child: _userInfo.value == null
? ElevatedButton(
onPressed: () async {
_userInfo.value =
await Navigator.of(context).push(OnboardingFlow.route());
},
child: Text('GET STARTED'),
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Welcome, ${_userInfo.value.name}!',
style: TextStyle(fontSize: 48.0),
),
const SizedBox(height: 48.0),
Text(
'So, you are ${_userInfo.value.age} years old and this is your favorite color? Great!',
style: TextStyle(fontSize: 32.0),
),
],
),
),
);
}
}
// FLOW
class OnboardingFlow extends StatelessWidget {
static Route<UserInfo> route() {
return MaterialPageRoute(builder: (_) => OnboardingFlow());
}
@override
Widget build(BuildContext context) {
print('INFO: ${const UserInfo()}');
return Scaffold(
body: FlowBuilder<UserInfo>(
state: const UserInfo(),
onGeneratePages: (profile, pages) {
return [
MaterialPage(child: NameForm()),
if (profile.name != null) MaterialPage(child: AgeForm()),
if (profile.age != null) MaterialPage(child: ColorForm()),
];
},
),
);
}
}
// FORMS
class NameForm extends HookWidget {
@override
Widget build(BuildContext context) {
final _name = useState<String>();
return Scaffold(
appBar: AppBar(title: const Text('Name')),
body: Container(
padding: EdgeInsets.all(8.0),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
autofocus: true,
onChanged: (value) => _name.value = value,
decoration: InputDecoration(
labelText: 'Name',
hintText: 'Enter your name',
),
),
const SizedBox(height: 24.0),
RaisedButton(
child: const Text('Continue'),
onPressed: () {
if (_name.value.isNotEmpty) {
context
.flow<UserInfo>()
.update((info) => info.copyWith(name: _name.value));
}
},
)
],
),
),
);
}
}
class AgeForm extends HookWidget {
@override
Widget build(BuildContext context) {
final _age = useState<int>();
return Scaffold(
appBar: AppBar(title: const Text('Age')),
body: Container(
padding: EdgeInsets.all(8.0),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropdownButtonFormField<int>(
items: List.generate(
200,
(index) => DropdownMenuItem(
value: index,
child: Text(index.toString()),
),
),
onChanged: (value) => _age.value = value,
decoration: InputDecoration(
labelText: 'Age',
hintText: 'How old are you?',
),
),
const SizedBox(height: 24.0),
RaisedButton(
child: const Text('Continue'),
onPressed: () {
if (_age.value != null) {
context
.flow<UserInfo>()
.update((info) => info.copyWith(age: _age.value));
}
},
)
],
),
),
);
}
}
class ColorForm extends HookWidget {
@override
Widget build(BuildContext context) {
final _color = useState<Color>(Colors.amber);
return Scaffold(
appBar: AppBar(title: const Text('Favorite Color')),
body: Container(
padding: EdgeInsets.all(8.0),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ColorPicker(
pickerColor: _color.value,
onColorChanged: (value) => _color.value = value,
showLabel: true,
pickerAreaHeightPercent: 0.8,
),
const SizedBox(height: 24.0),
RaisedButton(
child: const Text('Continue'),
onPressed: () {
if (_color.value != null) {
context.flow<UserInfo>().complete(
(info) => info.copyWith(favoriteColor: _color.value));
}
},
)
],
),
),
);
}
}
// DOMAIN
@freezed
abstract class UserInfo with _$UserInfo {
const factory UserInfo({String name, int age, Color favoriteColor}) =
_UserInfo;
}