Flutter 多页表单体系结构

Flutter 多页表单体系结构,flutter,Flutter,我正在尝试构建一个系统来创建具有多个页面的表单。我的方法是把它分成三个不同的部分 表单页面:不同的表单页面(每个页面都有自己的逻辑来验证字段) ProjectFormContainer:在导航器中保存页面的容器页面 MultiPageFormController:用于管理表单页面之间导航的控制器 我已经设法在项目FormContainer中添加了一个多页面控制器的ChangeNotifierProvider取得了一些进展,但我不确定如何将单个表单逻辑与其余元素连接起来,以及如何为该模型创建一个

我正在尝试构建一个系统来创建具有多个页面的表单。我的方法是把它分成三个不同的部分

  • 表单页面:不同的表单页面(每个页面都有自己的逻辑来验证字段)
  • ProjectFormContainer:在导航器中保存页面的容器页面
  • MultiPageFormController:用于管理表单页面之间导航的控制器
  • 我已经设法在项目FormContainer中添加了一个多页面控制器的ChangeNotifierProvider取得了一些进展,但我不确定如何将单个表单逻辑与其余元素连接起来,以及如何为该模型创建一个合适的体系结构

    我希望你们能给我一些建议。提前谢谢


    这是一个基于的解决方案,作者是

    我使用了以下软件包:

    • 管理表单的流程
    • 要摆脱有状态的小部件并拥有更精简的代码库
    • 管理用户信息的不变性
    • [可选]

    如果查看文档,您将看到,它管理一组表单小部件,流程的每个步骤一个。一旦完成流程,它就会弹出窗口小部件,并返回带有用户信息的主页面

    1.流的创建
    完整的源代码,便于复制粘贴
    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;
    }