Flutter中具有多个步骤(向导样式)的表单-最佳方法

时间:2019-07-24 21:22:51

标签: forms flutter dart

我需要准备一个屏幕来为具有多个字段的表创建新记录。有些字段以先前的字段为条件。我最初的尝试包括一个TabController / TabBarView / Tabs设置,其中每个选项卡都是表单的一部分。

我有两个问题:

  • 我似乎无法在TabBarView中隐藏“标签”(图标/文本)本身(我想对“下一步”使用按钮)

  • 即使我同意这一点,如果我为每个选项卡构建一个小部件,由于每个表单都无法共享,因此我也需要为每个表单(每个选项卡一个)使用GlobalKey。

是否有另一种方法可以使用另一种小部件或方法来将表单分为几个步骤/屏幕,然后提交整个数据?

2 个答案:

答案 0 :(得分:5)

您可以使用“步进器小部件”。它可以使您从多个步骤制作垂直或水平向导。

Stepper({
  Key key,
  @required this.steps,
  this.physics,
  this.type = StepperType.vertical,
  this.currentStep = 0,
  this.onStepTapped,
  this.onStepContinue,
  this.onStepCancel,
  this.controlsBuilder,
})

您可以通过 Paul Halliday 遵循此示例 https://developer.school/flutter-how-to-use-the-stepper-widget/

答案 1 :(得分:0)

我认为我找到了一种更好的方法来处理此问题。我将完全放弃“选项卡”(尽管我认为可以在选项卡中“嵌入”此逻辑),因为“下一个/上一个”按钮可能就足够了。我以这种方式保留了一个表单(密钥),并且似乎在保存的时候可以很好地工作。

import 'package:flutter/material.dart';

final _formKey = GlobalKey<FormState>();

void main() => runApp(MyApp());


class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sandbox',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}


class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Sandbox'),
      ),
      body: Container(
         padding: EdgeInsets.all(8.0),
         color: Colors.white,
         child: FormWidget(),
      ),
    );
  }
}


class FormWidget extends StatefulWidget {


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

class _FormWidgetState extends State<FormWidget> {


  int _stepNumber = 1;

  final ctl_name = TextEditingController();
  final ctl_age = TextEditingController();
  final ctl_address = TextEditingController();
  final ctl_city = TextEditingController();

  void saveData(BuildContext context) {

    _formKey.currentState.save();

    print(ctl_name.text);
    print(ctl_age.text);
    print(ctl_address.text);
    print(ctl_city.text);

  }

  void nextPage(BuildContext context) {

    setState(() {
      if (_stepNumber == 1)
        _stepNumber = 2;
      else
        _stepNumber = 1;
    });
  }


  Column formOneBuilder(BuildContext context) {
    return Column(
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Container(
              padding: const EdgeInsets.all(8.0),
              decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
              width: double.infinity,
              child: Text("STEP 1")
          ),
        ),
        TextFormField(
          controller: ctl_name,
          decoration: const InputDecoration(
              labelText: 'Step 1 Name'
          ),
        ),
        TextFormField(
          controller: ctl_age,
          decoration: const InputDecoration(
              labelText: 'Step 2 Age'
          ),
        ),
        Center(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FlatButton(
                  color: Colors.blue,
                  child: Text('Next'),
                  onPressed: () {nextPage(context);} ,
                ),
                Padding(padding: EdgeInsets.only(left: 8)),
                FlatButton(
                  color: Colors.blue,
                  child: Text('Save'),
                  onPressed: () {saveData(context);} ,
                ),
              ],
            ),
          ),
        )
      ],
    );
  }


  Column formTwoBuilder(BuildContext context) {

    return Column(
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Container(
              padding: const EdgeInsets.all(8.0),
              decoration: BoxDecoration(border: Border.all(color: Colors.red)),
              width: double.infinity,
              child: Text("STEP 2"),
          ),
        ),
        TextFormField(
          controller: ctl_address,
          decoration: const InputDecoration(
              labelText: 'Step 2 Address'
          ),
        ),
        TextFormField(
          controller: ctl_city,
          decoration: const InputDecoration(
              labelText: 'Step 2 City'
          ),
        ),
        Center(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FlatButton(
                  color: Colors.blue,
                  child: Text('Previous'),
                  onPressed: () {nextPage(context);} ,
                ),
                Padding(padding: EdgeInsets.only(left: 8)),
                FlatButton(
                  color: Colors.blue,
                  child: Text('Save'),
                  onPressed: () {saveData(context);} ,
                ),
              ],
            ),
          ),
        )
      ],
    );

  }

  @override
  Widget build(BuildContext context) {

    switch (_stepNumber) {
      case 1:
          return Form(
              key: _formKey,
              child:
                this.formOneBuilder(context),
          );
          break;

      case 2:
        return Form(
              key: _formKey,
              child:
                this.formTwoBuilder(context),
        );
        break;
    }

  }

  void dispose() {

    ctl_address.dispose();
    ctl_age.dispose();
    ctl_city.dispose();
    ctl_name.dispose();

    super.dispose();

  }


}