Flutter与提供者和Riverpod组成表格

时间:2020-10-24 14:08:27

标签: flutter dart

我是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());
    }
  }
}

这是我的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());

2 个答案:

答案 0 :(得分:0)

您可以使用 TextEditingController

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

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

答案 1 :(得分:0)

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

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

  • _autoValidate:将其留在widget内部,由Hook处理
  • copyWith 内添加 FormCalculatorModelNew(可以轻松更新部分值)

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();
                    }
                  },
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}