【问题标题】:Flutter make a form with provider and riverpodFlutter 用 provider 和 riverpod 制作表单
【发布时间】:2021-02-07 08:34:49
【问题描述】:

我是 Flutter 的新手,我想升级我的代码。我有一个使用多个 textformfields 的表单,我想使用 provider 和 riverpod 转换此代码以提高可读性,但我不知道该怎么做。 例如,我将代码简化为只有一个距离场,但还有很多其他的。

这是我的 CalculatorScreen :

import 'dart:async' show Future;
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:app/core/models/model_form_calculator.dart';
import 'package:app/core/services/service_form_validator.dart';
import 'package:app/core/utils/utils_app_color.dart';

class CalculatorScreen extends StatefulWidget
{
  CalculatorScreen({Key key}) : super(key: key);

  @override
  _CalculatorScreenState createState() => _CalculatorScreenState();
}

class _CalculatorScreenState extends State<CalculatorScreen>
{
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  final _formKey = GlobalKey<FormState>();

  FormCalculatorModel _formData = FormCalculatorModel();
  bool _autoValidateForm = false;

  final TextEditingController _controllerDistance = TextEditingController();

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose()
  {
    _controllerDistance.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context)
  {
    return GestureDetector(
      onTap: (() => FocusScope.of(context).requestFocus(FocusNode())),
      child: Scaffold(
          key: _scaffoldKey,
          backgroundColor: AppColors.colorBgDark,
          body : _buildBody()
      ),
    );
  }

  Widget _buildBody()
  {
    return SingleChildScrollView(
      child: Column(
        children: [
          Form(
            key: _formKey,
            autovalidate: _autoValidateForm,
            child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  TextFormField(
                    controller: _controllerDistance,
                    keyboardType: TextInputType.number,
                    decoration: InputDecoration(
                      hintText: "Enter a value",
                    ),
                    validator: (value){
                      return FormValidatorService.isDistanceValid(value);
                    },
                    onSaved: (var value) {
                      _formData.distance = num.tryParse(value).round();
                    },
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      Expanded(
                        child: FlatButton(
                            child: Text("Erase"),
                            onPressed: _buttonResetAction
                        ),
                      ),
                      Expanded(
                        child: FlatButton(
                            child: Text("Send"),
                            onPressed: _buttonSubmitAction
                        ),
                      ),
                    ],
                  ),
                ]
            ),
          ),
        ],
      ),
    );
  }

  void _buttonResetAction()
  {
    _eraseForm();
  }

  void _eraseForm(){
    setState(() {
      _formKey.currentState.reset();
      _formData = FormCalculatorModel();
      _autoValidateForm = false;
      _controllerDistance.clear();
    });
  }

  void _buttonSubmitAction() async
  {
    if (!_formKey.currentState.validate()) {
      setState(() {
        _autoValidateForm = true;
      });
      return;
    }
    _formKey.currentState.save();

    try{
      // some actions
    }catch(e){
      _eraseForm();
      print(e.toString());
    }
  }
}

This is my formModel(此模型包含我可以填写表单的所有字段,并允许我存储表单的值,一旦验证,然后使用这些值进行计算 ):


class FormCalculatorModel{
  int distance;

  FormCalculatorModel({
    this.distance,
 
  });

  @override
  String toString() {
    return '{ '
        '${this.distance}, '
    '}';
  }

}

还有我的 FormValidatorService :

class FormValidatorService{

  static String isDistanceValid(String value)
  {
    num _distance = num.tryParse(value);
    if (_distance == null) {
      return "is required";
    }
    if (_distance < 200) {
      return "Min distance is 200";
    }
    if (_distance > 1000) {
      return "Max dist is 1000";
    }
    return null;
  }
}

现在我想用riverpod 转换它。我有点迷茫,互联网上的例子很少,我真的不知道如何管理我的表格 起初我只是尝试处理表单的验证,但它不起作用。

我的计算器屏幕:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class CalculatorScreen extends HookWidget{

  final _formKey = GlobalKey<FormState>();
  bool _autoValidateForm = false;
  FormCalculatorModel _formData = FormCalculatorModel();
  final TextEditingController _controllerDistance = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: (() => FocusScope.of(context).requestFocus(FocusNode())),
      child: Scaffold(
          body : _buildBody(context)
      ),
    );
  }

  Widget _buildBody(BuildContext context){

    final _formModel = useProvider(formCalculatorProvider.state);

    return SingleChildScrollView(
      child: Column(
        children: [
          TitleComponent(
            title: "Calcul de charge",
            description: "Parametrer l'environnement de tir",
          ),
          ContainerComponent(
            background: AppColors.colorBgLight,
            children: [
              Form(
                key : _formKey,
                autovalidate: _autoValidateForm,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    TextFormField(
                      decoration: InputDecoration(
                        labelText: "Distance",
                        //errorText: _formModel.distance.error,
                      ),
                      controller: _controllerDistance,
                      validator: (String value){
                        return FormValidatorService.isDistanceValid(value);
                      },
                      onSaved: (var value) {_formData.distance = num.tryParse(value).round();}
                    ),
                  ],
                ),
              ),
              ButtonComponent.primary(
                  text: "Calculer",
                  context: context,
                  onPressed : context.read(formCalculatorProvider).submitData(key: _formKey),
              ),
            ],
          )
        ],
      ),
    );
  }
}

还有我的 FormCalculatorNotifier :

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

enum FormState
{
  EMPTY,
  SUCCESS,
  ERROR
}

class FormCalculatorModelNew {
  const FormCalculatorModelNew({this.formState, this.autoValidate, this.distance});
  final FormState formState;
  final bool autoValidate;
  final String distance;
}

class FormCalculatorNotifier extends StateNotifier<FormCalculatorModelNew>
{
  FormCalculatorNotifier() : super(_initial);

  static const FormState _initialState = FormState.EMPTY;
  static const _initial = FormCalculatorModelNew(
      formState : _initialState,
      autoValidate: false,
      distance: null
  );

   submitData({key}){
     print(key);
     if (!key.currentState.validate()) {
       state = FormCalculatorModelNew(
           autoValidate: true,
       );
       return;
     }
     key.currentState.save();
  }

}

提供者:

final formCalculatorProvider = StateNotifierProvider((ref) => FormCalculatorNotifier());

【问题讨论】:

  • 这方面有什么进展吗?您还可以添加验证。

标签: flutter dart riverpod


【解决方案1】:

在您的示例代码中使用 Provider 并没有什么意义,因为我看不到任何地方监听 formCalculatorProvider 的状态。此外,表单本身应在表单小部件本身中进行管理。

我假设您想与其他小部件共享距离值。以下是我将要做的:

  • _autoValidate: 留在widget里面,用Hook处理
  • FormCalculatorModelNew中添加copyWith(可以轻松更新部分值)

formCalculatorProvider部分:

final formCalculatorProvider = StateNotifierProvider((ref) => FormCalculatorNotifier());

enum MyFormState { EMPTY, SUCCESS, ERROR }

class FormCalculatorModelNew {
  const FormCalculatorModelNew({this.formState, this.distance});

  final MyFormState formState;
  final int distance;

  FormCalculatorModelNew copyWith({
    MyFormState formState,
    int distance,
  }) {
    return FormCalculatorModelNew(
      formState: formState ?? this.formState,
      distance: distance ?? this.distance,
    );
  }
}

class FormCalculatorNotifier extends StateNotifier<FormCalculatorModelNew> {
  FormCalculatorNotifier() : super(_initial);

  static const MyFormState _initialState = MyFormState.EMPTY;
  static const _initial =
      FormCalculatorModelNew(formState: _initialState, distance: null);

  void update(int distance) {
    state = state.copyWith(distance: distance, formState: MyFormState.SUCCESS);
  }

  void error() {
    state = state.copyWith(distance: null, formState: MyFormState.ERROR);
  }

  void clear() {
    state = state.copyWith(distance: null, formState: MyFormState.EMPTY);
  }
}

CalculatorScreen部分:(简化)

class CalculatorScreen extends HookWidget {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    final _autoValidate = useState<bool>(false);
    final _controller = useTextEditingController();

    return Scaffold(
      body: Form(
        key: _formKey,
        autovalidate: _autoValidate.value,
        child: Column(
          children: [
            TextFormField(
              controller: _controller,
              keyboardType: TextInputType.number,
              validator: (value) {
                return FormValidatorService.isDistanceValid(value);
              },
              onSaved: (value) {
                context.read(formCalculatorProvider).update(num.tryParse(value).round());
              },
            ),
            Row(
              children: [
                FlatButton(
                  child: Text('Erase'),
                  onPressed: () {
                    _formKey.currentState.reset();
                    _controller.clear();
                    _autoValidate.value = false;
                    context.read(formCalculatorProvider).clear();
                  },
                ),
                FlatButton(
                  child: Text('Send'),
                  onPressed: () {
                    if(_formKey.currentState.validate()){
                      _formKey.currentState.save();
                    }else{
                      _autoValidate.value = true;
                      context.read(formCalculatorProvider).error();
                    }
                  },
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

【讨论】:

  • 想象一下我们正在逐步处理表单(使用步进式工作流程)在父级中呈现具有不同值的相同小部件并使用 Riverpod 处理其状态。
【解决方案2】:

您可以使用TextEditingController

像这样进一步创建一个提供程序,您现在可以收听文本更改并使用相同的提供程序将它们存储在所需的位置

final formControllerProvider =
StateProvider<TextEditingController>((ref) => TextEditingController());

【讨论】:

  • 如何将formControllerProvider生成的控制器分配给“TextFormField”控制器字段?
  • 我认为这不能正确处理文本编辑更新,您需要改用ChangeNotifierProvider 吗?
猜你喜欢
  • 2021-05-06
  • 2023-02-07
  • 2021-01-25
  • 2021-03-17
  • 1970-01-01
  • 2021-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多