4

Flutter は初めてで、コードをアップグレードしたいと考えています。複数の textformfields を使用するフォームがあり、読みやすさを改善するためにプロバイダーと riverpod を使用してこのコードを変換したいのですが、その方法がわかりません。この例では、コードを 1 つの距離フィールドのみに単純化しましたが、他にも多くの距離フィールドがあります。

これは私の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());

4

2 に答える 2

1

formCalculatorProviderの状態をリッスンする場所がどこにもないため、コード例でProviderを使用するのは本当に意味がありません。また、フォーム自体はフォーム ウィジェット自体で管理する必要があります。

距離の値を他のウィジェットと共有したいと思います。これが私がすることです:

  • _autoValidate: ウィジェット内に残してフックで処理
  • 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);
  }
}

電卓画面部分:(簡略化)

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();
                    }
                  },
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
于 2021-04-25T09:06:16.210 に答える