Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

flutter_redux with form fields #21

Closed
arthurgustin opened this issue Mar 12, 2018 · 10 comments
Closed

flutter_redux with form fields #21

arthurgustin opened this issue Mar 12, 2018 · 10 comments

Comments

@arthurgustin
Copy link

Hi, I'm new to flutter/dart and I'm developing a simple use-case: 1 form across 2 pages
I want to persist data and be able to get the first screen data when I'm on the second view. After hours of research, I found this library and I can't get it to work properly for my case.
Can you provide a simple example with forms and dart 2.0 ? I think my issue is maybe related to #20, but mostly because I'm a beginner in the language.
It's all so foggy to me with controllers, focusNode, event propagation... I'm a backend guy, I'm used to simpler things.
My code is on this branch, I'm sorry for the mess, its a poc for now...

https://github.com/Vinubaba/OM-App/blob/230658b4defb1ee7244788cd2e63398fc3d8992f/lib/main.dart#L252-L279

I've tried to understand your example on gitlab, but I don't understand every part of it

@brianegan
Copy link
Owner

Hey @arthurgustin -- thanks for writing in! Yah, text fields are a bit of a tricky one overall! No worries about the confusion :)

I'll try to find time to check out your code tonight after I've wrapped up work.

@arthurgustin
Copy link
Author

@brianegan Thanks man :)

@arthurgustin
Copy link
Author

Hey @brianegan , any new on this topic ? If you don't have time for this, maybe you can give me a pointer to a viable alternative (if it exists) ?

@brianegan
Copy link
Owner

brianegan commented Mar 13, 2018

Hey @arthurgustin, just got a chance to look through the code! So, it looks like you've got things hooked up in slightly the wrong way. Looking at your code, #20 should not be the problem.

One fundamental concept to understand: In order to share state between Screens, you need to lift that state above all of your screens! In Flutter, this means wrapping MaterialApp in an InheritedWidget (such as the StoreProvider) or a StatefulWidget.

Right now, you're only wrapping your first screen with a StoreProvider. This means that any children of the first screen will have access to the Store, but siblings (such as different pages) will NOT have access to the store. This is why you might get a null error when trying to access the store on the second page!

Also, no need to creat your own Store singleton. That's really the job of the StoreProvider: hand it a Store and it will hold it as a singleton and pass it around!

Sorry, didn't have time to clean this up, but here's a working example:

import 'dart:async';

import 'package:fluro/fluro.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:intl/intl.dart';
import 'package:meta/meta.dart';
import 'package:redux/redux.dart';

class SetFirstNameAction {
  final String firsName;

  SetFirstNameAction(this.firsName);
}

String firstNameReducer(String state, dynamic action) {
  if (action is SetFirstNameAction) {
    return action.firsName;
  }

  return state;
}

class Child {
  String firstName;
}

class Routes {
  static final Router router = configureRoutes(new Router());

  static Router configureRoutes(Router router) {
    // Define our home page.
    router.define('/', handler: new Handler(
        handlerFunc: (BuildContext context, Map<String, dynamic> params) {
      return new HomePage();
    }));

    // Define our about page.
    router.define('/kids/register/1', handler: new Handler(
        handlerFunc: (BuildContext context, Map<String, dynamic> params) {
      return new MyInheritedWidget(
        data: MediaQuery.of(context),
        child: new RegisterKidPage1(),
      );
    }));

    // Define our about page.
    router.define('/kids/register/2', handler: new Handler(
        handlerFunc: (BuildContext context, Map<String, dynamic> params) {
      return new MyInheritedWidget(
        data: MediaQuery.of(context),
        child: new RegisterKidPage2(),
      );
    }));
    return router;
  }
}

void main() {
  runApp(new StoreProvider(
    store: new Store<String>(firstNameReducer, initialState: ""),
    child: new MaterialApp(
      onGenerateRoute: Routes.router.generator,
    ),
  ));
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
            title: new Text("Teddycare HomePage"),
            backgroundColor: Colors.deepOrange),
        body: new Container(
            child: new Center(
                child: new Column(
          children: [
            new RaisedButton(
              child: new Text('Registration page'),
              onPressed: () {
                Navigator.of(context).pushNamed("/kids/register/1");
              },
            ),
          ],
        ))));
  }
}

class MyInheritedWidget extends InheritedWidget {
  static MyInheritedWidget of(BuildContext context) =>
      context.inheritFromWidgetOfExactType(MyInheritedWidget);

  final MediaQueryData _data;

  String get deviceWidth => _data.size.width.toString();

  Orientation get orientation => _data.orientation;

  MyInheritedWidget({
    Key key,
    MediaQueryData data,
    Widget child,
  })
      : _data = data,
        super(key: key, child: child);

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) =>
      _data != oldWidget._data;
}

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

  @override
  _RegisterKidPage1 createState() => new _RegisterKidPage1();
}

class _RegisterKidPage1 extends State<RegisterKidPage1> {
  var numberOfHorizontalCell = 2;
  var ratio = 4.5;

  TextEditingController childFirstNameController = new TextEditingController();
  FocusNode childFirstNameFocus = new FocusNode();

  Widget _buildForm(BuildContext context) {
    return new Form(
        child: new Container(
            child: new Column(
      mainAxisSize: MainAxisSize.max,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        new Flexible(
          child: new GridView.count(
              crossAxisCount: numberOfHorizontalCell,
              childAspectRatio: ratio,
              padding: const EdgeInsets.all(4.0),
              mainAxisSpacing: 4.0,
              crossAxisSpacing: 4.0,
              children: [
                new StoreConnector<String, String>(
                  converter: (store) => store.state,
                  builder: (context, count) => new ListTile(
                        title: new TextFormField(
                          decoration: new InputDecoration(
                            hintText: "Firstname",
                          ),
                          controller: childFirstNameController,
                          focusNode: childFirstNameFocus,
                          initialValue: count,
                          //onChanged: _setFirstName,
                          // initialValue: child.firstName,
                        ),
                      ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Lastname",
                    ),
                  ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Relationship",
                    ),
                  ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Phone",
                    ),
                  ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Address Line 1",
                    ),
                  ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Address Line 2",
                    ),
                  ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "City",
                    ),
                  ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "State",
                    ),
                  ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Zip",
                    ),
                  ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Email Address",
                    ),
                  ),
                ),
              ]),
        ),
        new Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            new Padding(
              padding: const EdgeInsets.all(16.0),
              child: new RaisedButton(
                onPressed: () {
                  new StoreProvider.of(context).store.dispatch(
                      new SetFirstNameAction(childFirstNameController.text));
                  Routes.router.navigateTo(context, "/kids/register/2",
                      transition: TransitionType.inFromLeft);
                  //Navigator.of(context).pushNamed("/kids/register/2");
                },
                child: new Text('Next'),
              ),
            )
          ],
        )
      ],
    )));
  }

  @override
  Widget build(BuildContext context) {
    if (MyInheritedWidget.of(context).orientation == Orientation.portrait) {
      numberOfHorizontalCell = 1;
    } else {
      numberOfHorizontalCell = 2;
    }

    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Step 1: Family Details'),
      ),
      body: _buildForm(context),
      resizeToAvoidBottomPadding: true,
    );
  }
}

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

  @override
  _RegisterKidPage2 createState() => new _RegisterKidPage2();
}

class _RegisterKidPage2 extends State<RegisterKidPage2> {
  var numberOfHorizontalCell = 2;
  var ratio = 4.5;

  String radioValue = "male";
  bool addressSameAsParent = false;

  var _datetime = new DateTime.now();

  // Age Range dropdown
  dynamic ddAgeRange;

  // Allergies
  bool chkAllergyDairy = false;
  bool chkAllergyNuts = false;
  bool chkAllergyGluten = false;

  // Special instructions
  bool chkVegetarian = false;
  bool chkBringsOwnFood = false;

  void handleRadioValueChanged(String value) {
    setState(() {
      radioValue = value;
    });
  }

  void handleCheckboxValueChanged(bool value) {
    setState(() {
      addressSameAsParent = value;
    });
  }

  void chkAllergyDairyChanged(bool value) {
    setState(() {
      chkAllergyDairy = value;
    });
  }

  void chkAllergyNutsChanged(bool value) {
    setState(() {
      chkAllergyNuts = value;
    });
  }

  void chkAllergyGlutenChanged(bool value) {
    setState(() {
      chkAllergyGluten = value;
    });
  }

  void chkVegetarianChanged(bool value) {
    setState(() {
      chkVegetarian = value;
    });
  }

  void chkBringsOwnFoodChanged(bool value) {
    setState(() {
      chkBringsOwnFood = value;
    });
  }

  void ddAgeRangeChanged(dynamic value) {
    setState(() {
      ddAgeRange = value;
    });
  }

  void handleDropDownValueChanged(dynamic value) {
    setState(() {});
  }

  void uploadPictureButton() {}

  void _submit() {}

  Widget _buildForm() {
    return new Form(
        child: new Container(
            child: new Column(
      mainAxisSize: MainAxisSize.max,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        new Flexible(
          child: new GridView.count(
              crossAxisCount: numberOfHorizontalCell,
              childAspectRatio: ratio,
              padding: const EdgeInsets.all(4.0),
              mainAxisSpacing: 4.0,
              crossAxisSpacing: 4.0,
              children: [
                new ListTile(
                  title: new TextFormField(
                    initialValue: new StoreProvider.of(context).store.state,
                    decoration: new InputDecoration(
                      hintText: "Firstname",
                    ),
                  ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Lastname",
                    ),
                  ),
                ),
                new Row(
                  children: <Widget>[
                    new Flexible(
                        child: new ListTile(
                      title: new TextFormField(
                        decoration: new InputDecoration(
                          hintText: "BirthDate",
                        ),
                      ),
                    )),
                    new Flexible(
                        child: new ListTile(
                      title: new TextFormField(
                        decoration: new InputDecoration(
                          hintText: "Birth month",
                        ),
                      ),
                    )),
                    new Flexible(
                        child: new ListTile(
                      title: new TextFormField(
                        decoration: new InputDecoration(
                          hintText: "Birth Year",
                        ),
                      ),
                    )),
                  ],
                ),
                new Row(children: [
                  new Flexible(
                      child: new DropdownButton(
                          onChanged: ddAgeRangeChanged,
                          hint: new Text("zdzd"),
                          value: ddAgeRange,
                          items: [
                        new DropdownMenuItem(
                          child: new Text("2 years"),
                        )
                      ]))
                ]),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Class Name",
                    ),
                  ),
                ),
                new ListTile(
                  title: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Teacher Name",
                    ),
                  ),
                ),
                new Row(
                  children: <Widget>[
                    new Radio<String>(
                        value: "Male",
                        groupValue: radioValue,
                        onChanged: handleRadioValueChanged),
                    new Text("Male"),
                    new Divider(),
                    new Radio<String>(
                        value: "Female",
                        groupValue: radioValue,
                        onChanged: handleRadioValueChanged),
                    new Text("Female"),
                  ],
                ),
                new Row(
                  children: [
                    new Checkbox(
                        value: addressSameAsParent,
                        onChanged: handleCheckboxValueChanged),
                    new Text("Same address as "),
                    new Flexible(
                      child: new DropdownButton(
                        onChanged: handleDropDownValueChanged,
                        items: new List.generate(20, (int index) {
                          return new DropdownMenuItem(
                              child: new Container(
                            child: new Text("Item#$index"),
                          ));
                        }),
                      ),
                    ),
                  ],
                ),
                new Text(
                  "Allergies",
                  textAlign: TextAlign.left,
                  overflow: TextOverflow.ellipsis,
                  style: new TextStyle(
                      fontWeight: FontWeight.bold, fontSize: 25.5),
                ),
                new Row(
                  children: [
                    new Checkbox(
                        value: chkAllergyDairy,
                        onChanged: chkAllergyDairyChanged),
                    new Text("Dairy"),
                    new Checkbox(
                        value: chkAllergyNuts,
                        onChanged: chkAllergyNutsChanged),
                    new Text("Nuts"),
                    new Checkbox(
                        value: chkAllergyGluten,
                        onChanged: chkAllergyGlutenChanged),
                    new Text("Gluten"),
                  ],
                ),
                new Container(
                  decoration: new BoxDecoration(
                      border: new Border.all(color: Colors.blueAccent)),
                  child: new TextFormField(
                    decoration: new InputDecoration(
                      hintText:
                          "For EACH allergy, explain what the parent/guardian wants to do (if there is a reaction)",
                    ),
                    maxLines: 5,
                    keyboardType: TextInputType.multiline,
                  ),
                ),
                new Text(
                  "Special Instructions",
                  textAlign: TextAlign.left,
                  overflow: TextOverflow.ellipsis,
                  style: new TextStyle(
                      fontWeight: FontWeight.bold, fontSize: 25.5),
                ),
                new Row(
                  children: [
                    new Checkbox(
                        value: chkVegetarian, onChanged: chkVegetarianChanged),
                    new Text("Vegetarian"),
                    new Checkbox(
                        value: chkBringsOwnFood,
                        onChanged: chkBringsOwnFoodChanged),
                    new Text("Brings own food"),
                  ],
                ),
                new Row(
                  children: [
                    new Text(
                      "Picture",
                      textAlign: TextAlign.left,
                      overflow: TextOverflow.ellipsis,
                      style: new TextStyle(
                          fontWeight: FontWeight.bold, fontSize: 25.5),
                    ),
                    new RaisedButton(
                        onPressed: uploadPictureButton,
                        child: new Text("Upload"))
                  ],
                ),
                new Container(
                  decoration: new BoxDecoration(
                      border: new Border.all(color: Colors.blueAccent)),
                  child: new TextFormField(
                    decoration: new InputDecoration(
                      hintText: "Notes",
                    ),
                    maxLines: 6,
                    keyboardType: TextInputType.multiline,
                  ),
                ),
                new Text(
                  "Start Date",
                  textAlign: TextAlign.left,
                  overflow: TextOverflow.ellipsis,
                  style: new TextStyle(
                      fontWeight: FontWeight.bold, fontSize: 25.5),
                ),
                new Row(
                  children: [
                    new Flexible(
                      child: new DateTimeItem(
                        dateTime: _datetime,
                        onChanged: (dateTime) =>
                            setState(() => _datetime = dateTime),
                      ),
                    ),
                  ],
                ),
              ]),
        ),
        new Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            new Padding(
              padding: const EdgeInsets.all(16.0),
              child: new RaisedButton(
                onPressed: _submit,
                child: new Text('Done'),
              ),
            )
          ],
        )
      ],
    )));
  }

  @override
  Widget build(BuildContext context) {
    if (MyInheritedWidget.of(context).orientation == Orientation.portrait) {
      numberOfHorizontalCell = 1;
    } else {
      numberOfHorizontalCell = 2;
    }

    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Step 2: Kid's Details"),
      ),
      body: _buildForm(),
      resizeToAvoidBottomPadding: true,
    );
  }
}

/*


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

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var _ipAddress = 'Unknown';

  _getIPAddress() async {

    HttpClient client = new HttpClient();
    String result;

    client.getUrl(Uri.parse("http://35.205.143.130/api/v1/children"))
        .then((HttpClientRequest request) {
      request.headers.add("Authorization", "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjIzZTIyYTQ3NjZmYjYyNzMxN2ZjOGRjN2M5YjRkM2M3ZmY4YTg1NjcifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vdGVkZHljYXJlLTE5MzkxMCIsIm5hbWUiOiJBcnRodXIgR3VzdGluIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS8tWGRVSXFkTWtDV0EvQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvNDI1MnJzY2J2NU0vcGhvdG8uanBnIiwiYWRtaW4iOnRydWUsImFkdWx0IjpmYWxzZSwib2ZmaWNlbWFuYWdlciI6ZmFsc2UsInRlYWNoZXIiOmZhbHNlLCJ1c2VySWQiOiJlOTU2OWY5ZC0yMmVkLTQyOTctOTAwOC0wMzlkMTIwOWEzMmEiLCJhdWQiOiJ0ZWRkeWNhcmUtMTkzOTEwIiwiYXV0aF90aW1lIjoxNTIwMTk3ODU1LCJ1c2VyX2lkIjoid0FnVUpQQ0IzRk92aDVEck1XanZMTm45bzIwMiIsInN1YiI6IndBZ1VKUENCM0ZPdmg1RHJNV2p2TE5uOW8yMDIiLCJpYXQiOjE1MjAxOTc4NTYsImV4cCI6MTUyMDIwMTQ1NiwiZW1haWwiOiJhcnRodXIuZ3VzdGluQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7Imdvb2dsZS5jb20iOlsiMTE0MjQxNDU1NTI0MTUyMzUyMTUwIl0sImVtYWlsIjpbImFydGh1ci5ndXN0aW5AZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ29vZ2xlLmNvbSJ9fQ.hEnV0_kA91dY5ZxAhbZahvPHlCGF97DCUSIc0ASUfsCzzNRg-gCL9u3AELMc6kSxVB8FYSKq_hdg7uyP8P-sSIlto4ssdkXtUWJp8DfULo2R1Ya-fPOB4bjRVGL4XiX3i2Vksq2XK6GEmHIg1vjNTtq401rdAIS7mOXcfYEu9GdPL2OyuV6EdmZvrHMuFGdlziTUiv8Xzviy1bvIxJc2cVkLyUAMOUZuT6kGBxPKtXIG6GJccH6dOL07W9NJBDaC_BS5lklt-H7lWTMsqfPcT3322oIAftGUVfjmuSAzUwPN0CID-xncD8v0eCe_doEdWlQp3LYsAVKhRRoM_4j1TQ");

      // Optionally write to the request object...
      // Then call close.
      return request.close();
    }).then((HttpClientResponse response) {
      if (response.statusCode == HttpStatus.OK) {

        response.transform(UTF8.decoder).listen((contents) {
          // handle data
          var decoded = JSON.decode(contents);
          print(decoded);
          var user = new Child.fromJson(decoded);
          print(user);
        });
      }else {
        result =
        'Error getting children:\nHttp status ${response.statusCode}';
      }
    });

    // If the widget was removed from the tree while the message was in flight,
    // we want to discard the reply rather than calling setState to update our
    // non-existent appearance.
    if (!mounted) return;

    setState(() {
      _ipAddress = result;
    });
  }

  @override
  Widget build(BuildContext context) {
    var spacer = new SizedBox(height: 32.0);

    return new Container(
      child: new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          new Text('Children:'),
          new Text('$_ipAddress.'),
          spacer,
          new RaisedButton(
            onPressed: _getIPAddress,
            child: new Text('Get children'),
          ),
        ],
      ),
    );
  }
}


*/

class DateTimeItem extends StatelessWidget {
  DateTimeItem({Key key, DateTime dateTime, @required this.onChanged})
      : assert(onChanged != null),
        date = dateTime == null
            ? new DateTime.now()
            : new DateTime(dateTime.year, dateTime.month, dateTime.day),
        time = dateTime == null
            ? new DateTime.now()
            : new TimeOfDay(hour: dateTime.hour, minute: dateTime.minute),
        super(key: key);

  final DateTime date;
  final TimeOfDay time;
  final ValueChanged<DateTime> onChanged;

  @override
  Widget build(BuildContext context) {
    return new Row(
      children: <Widget>[
        new Expanded(
          child: new InkWell(
            onTap: (() => _showDatePicker(context)),
            child: new ListTile(
              title: new Text(new DateFormat('EEEE, MMMM d').format(date)),
              leading: new IconButton(
                icon: new Icon(Icons.calendar_today),
                onPressed: () {},
              ),
            ),
          ),
        ),
        /* new InkWell(
          onTap: (() => _showTimePicker(context)),
          child: new Padding(
              padding: new EdgeInsets.symmetric(vertical: 8.0),
              child: new Text('$time')),
        ),*/
      ],
    );
  }

  Future _showDatePicker(BuildContext context) async {
    DateTime dateTimePicked = await showDatePicker(
        context: context,
        initialDate: date,
        firstDate: date.subtract(const Duration(days: 20000)),
        lastDate: new DateTime.now());

    if (dateTimePicked != null) {
      onChanged(new DateTime(dateTimePicked.year, dateTimePicked.month,
          dateTimePicked.day, time.hour, time.minute));
    }
  }

  Future _showTimePicker(BuildContext context) async {
    TimeOfDay timeOfDay =
        await showTimePicker(context: context, initialTime: time);

    if (timeOfDay != null) {
      onChanged(new DateTime(
          date.year, date.month, date.day, timeOfDay.hour, timeOfDay.minute));
    }
  }
}

@arthurgustin
Copy link
Author

Thanks @brianegan ! I can't test now since I'm at work but I think I understand what the solution is.
I have another question and it's related to this topic so I don't want to open another issue.
How would you handle all the fields in the form ? My idea is that (correct me if I'm wrong):

  • 1 global store for all my application
  • 1 reducer per use case: in my poc I want to store all the informations in both pages, then wrap it up and send a json request
  • The reducer needs to update fields in a specific class ? Or maybe it's inside the action like the following code ?
class MyAction {
  final String firstName;
  final String lastName;
  final String adress_1;
  ...

  MyAction(this.firstName, this.lastName, this.adress_1);
}

Once I understand this I will have a cleaning phase of those 500 lines, I promise !

@arthurgustin
Copy link
Author

Or the state of the store would be my class embedding all my attributes ?

@brianegan
Copy link
Owner

  • Yep, 1 global store for your application!
  • The Reducer should intercept actions and update the state of the app.
  • Actions Send data to the Reducer / Middleware layer.

In general, I'd probably start with this approach and see if you run into problems: https://github.com/brianegan/flutter_architecture_samples/blob/master/example/redux/lib/presentation/add_edit_screen.dart#L40

Basically, in this code:

  1. Create text controllers for each textformfield in your form on Screen 1, as well as necessary validation logic
  2. On the "next" button, validate the form. If it's valid, dispatch an action with the form data and navigate to the next screen.
  3. In your Reducer, intercept this action and update the app state.
  4. Your Second Screen will launch. You can pull the current values you need from the Store and set the initialValue of the TextFormField.
  5. Validate and Dispatch on the Second Screen
  6. Write a Middleware function that intercepts the action from the second screen, it will call your API with all the necessary data.

Hope that helps!

@brianegan
Copy link
Owner

Thinking about this a bit more, if you only need redux for this use-case, might be easier to just work with an InheritedWidget. You could follow the same general pattern, but rather than dispatching actions and all that, you could just set fields on your InheritedWidget.

@brianegan
Copy link
Owner

@arthurgustin Anything else I can help with here? Cool to close?

@arthurgustin
Copy link
Author

Hi @brianegan,
I made something working, but I could not get rid of text controllers inside the form because when I switch page or I scroll, widgets get cleared because they are redraw (this is what I've understand by looking at forums).
In the end, there are a lot of boilerplate, but it works. You can close this issue :) But I will open another because I have another question !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants