1

I am new to flutter and I wanted to manage a login screen page that has one text field and when the user click next I save the value and change the hint text to the next step hint on the same text field using scoped model state management.

first here is my code:

loginBase.dart(the main login page class that has both "login field (imported)" and "login button"):

import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:scoped_model/scoped_model.dart';
import 'login-model.dart';

//pages
import './LoginFields.dart';

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {

  @override
  Widget build(BuildContext context) {
    return ScopedModel<LoginModel>(
      model: LoginModel(),
      child: Scaffold(
        body: Material(
          child: Container(
            color: Colors.white,
            child: ListView(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(top: 20.0, bottom: 20.0),
                  child: Image(
                      image: AssetImage("assets/images/landing_screen.jpg")),
                ),
                LoginFields(),
                Padding(
                  padding: const EdgeInsets.only(
                      left: 10.0, right: 10.0, top: 100.0),
                  child: Row(
                    children: <Widget>[
                      Expanded(
                          flex: 1,
                          child: Padding(
                            padding:
                                const EdgeInsets.only(left: 15.0, right: 15.0),
                            child: buildButton(),
                          )),
                    ],
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }

  var buildButton = () => ScopedModelDescendant<LoginModel>(
        builder: (context, child, model) => FlatButton(
              onPressed: () {
                model.nextStep();
              },
              color: Colors.black,
              shape: new RoundedRectangleBorder(
                  borderRadius: new BorderRadius.circular(30.0)),
              child: Padding(
                  padding: const EdgeInsets.all(14.0),
                  child: Icon(
                    Icons.arrow_forward,
                    color: Colors.white,
                    size: 25.0,
                  )),
            ),
      );
}

LoginFields.dart (the one containing the field):

import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import 'login-model.dart';

class LoginFields extends StatefulWidget {
  @override
  _LoginFieldsState createState() => _LoginFieldsState();
  LoginFields();
}

class _LoginFieldsState extends State<LoginFields> {
  @override
  Widget build(BuildContext context) {
    return ScopedModel<LoginModel>(
      model: LoginModel(),
      child: Container(
        child: Column(
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(left: 30.0, right: 30.0),
              child: buildTextField(),
            ),
          ],
        ),
      ),
    );
  }

  var buildTextField = () => ScopedModelDescendant<LoginModel>(
        builder: (context, child, model) => TextFormField(
              decoration: InputDecoration(
                  hintText: model.hintText,
                  enabledBorder: UnderlineInputBorder(
                      borderSide:
                          BorderSide(color: Color(0xFFE4E4E4), width: 2.0))),
              textAlign: TextAlign.center,
              controller: model.controller,
            ),
      );
}

and finally the login-model.dart (that contain the scoped model for the login page):

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

class LoginModel extends Model {
  //steps fields options
  String hintText = "How can we call you?";
  TextEditingController controller;
  int stepsCounter = 0;
  List<Step> steps = <Step>[
    Step(
        field: "username",
        controller: new TextEditingController(),
        hint: "How can we call you?"),
    Step(
        field: "email",
        controller: new TextEditingController(),
        hint: "Your Email goes here"),
    Step(
        field: "mobile",
        controller: new TextEditingController(),
        hint: "Phone number ex: +201234..."),
    Step(
        field: "password",
        controller: new TextEditingController(),
        hint: "your Password"),
  ];

  void initStep() {
    hintText = steps[0].hint;
    notifyListeners();
  }

  void nextStep() {
    if (stepsCounter <= 3) {
      steps[stepsCounter].controller=controller;
      print(controller);
      if (stepsCounter<3) {
        hintText = steps[stepsCounter + 1].hint;
      }
      stepsCounter++;
    } else {
      return;
    }
    notifyListeners();
  }
}

class Step {
  String field;
  TextEditingController controller;
  String hint;

  Step({this.field, this.controller, this.hint});
}

The issue is:

when I call the nextStep() function the hint text value change in the model but not updated in the login page, Am I applying it in a wrong way?

Pablo Cegarra
  • 20,955
  • 12
  • 92
  • 110
Mahmoud Mabrouk
  • 703
  • 2
  • 13
  • 31
  • 1
    as a rule of thumb `ScopeModel` and `ScopedModelDescendant` don't need a stateful widget to work. Probably that would solve your issue already. – 5422m4n Jun 06 '19 at 11:10

1 Answers1

1

I made some logical changes but this works and added features I think you were looking for. The problem is having a separate login_fields.dart class. The model is not talking to the login_base.dart. I have tested this and seems to work pretty well.

Update you login_base.dart:

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

import 'login_model.dart';

class LoginPage extends StatelessWidget {
  TextInputType getKeyboard(StepType type) {
    switch (type) {
      case StepType.email:
        return TextInputType.emailAddress;
        break;
      case StepType.phone:
        return TextInputType.phone;
        break;
      case StepType.username:
      case StepType.password:
        return TextInputType.text;
        break;
      default:
        return TextInputType.text;
    }
  }

  @override
  Widget build(BuildContext context) {
    return ScopedModel<LoginModel>(
      model: LoginModel(),
      child: Scaffold(
        body: Material(
          child: Container(
            color: Colors.white,
            child: ListView(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(top: 20.0, bottom: 20.0),
                  child: Image(
                      image: AssetImage("assets/images/landing_screen.jpg")),
                ),
                Padding(
                  padding:
                      const EdgeInsets.only(top: 50.0, left: 30.0, right: 30.0),
                  child: ScopedModelDescendant<LoginModel>(
                    builder: (context, child, model) => TextFormField(
                          autofocus: true,
                          decoration: InputDecoration(
                              hintText: model.steps[model.step].hint,
                              enabledBorder: UnderlineInputBorder(
                                  borderSide: BorderSide(
                                      color: Color(0xFFE4E4E4), width: 2.0))),
                          keyboardType:
                              getKeyboard(model.steps[model.step].type),
                          textAlign: TextAlign.center,
                          controller: model.steps[model.step].controller,
                          // textInputAction:
                          //     model.steps[model.step].type == StepType.password
                          //         ? TextInputAction.done
                          //         : TextInputAction.next,
                          obscureText:
                              model.steps[model.step].type == StepType.password,
                          onEditingComplete:
                              model.step == model.steps.length - 1
                                  ? null
                                  : model.nextStep,
                        ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.only(
                      left: 10.0, right: 10.0, top: 100.0),
                  child: Row(
                    children: <Widget>[
                      Expanded(
                          flex: 1,
                          child: Padding(
                            padding:
                                const EdgeInsets.only(left: 15.0, right: 15.0),
                            child: ScopedModelDescendant<LoginModel>(
                              builder: (context, child, model) => FlatButton(
                                    onPressed:
                                        model.step == model.steps.length - 1
                                            ? model.startOver
                                            : model.nextStep,
                                    color: Colors.black,
                                    shape: new RoundedRectangleBorder(
                                        borderRadius:
                                            new BorderRadius.circular(30.0)),
                                    child: Padding(
                                        padding: const EdgeInsets.all(14.0),
                                        child:
                                            model.step == model.steps.length - 1
                                                ? Text(
                                                    'Start Over',
                                                    style: Theme.of(context)
                                                        .textTheme
                                                        .copyWith(
                                                            button: TextStyle(
                                                          color: Colors.white,
                                                        ))
                                                        .button,
                                                  )
                                                : Icon(
                                                    Icons.arrow_forward,
                                                    color: Colors.white,
                                                    size: 25.0,
                                                  )),
                                  ),
                            ),
                          )),
                    ],
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

And your login_model.dart

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

enum StepType { username, email, phone, password }

class LoginModel extends Model {
  //steps fields options
  int _step = 0;

  int get step => _step;

  set step(int value) => _step = value;

  var steps = <Step>[
    Step(
        type: StepType.username,
        field: "username",
        controller: new TextEditingController(),
        hint: "How can we call you?"),
    Step(
        type: StepType.email,
        field: "email",
        controller: new TextEditingController(),
        hint: "Your Email goes here"),
    Step(
        type: StepType.phone,
        field: "mobile",
        controller: new TextEditingController(),
        hint: "Phone number ex: +201234..."),
    Step(
        type: StepType.password,
        field: "password",
        controller: new TextEditingController(),
        hint: "your Password"),
  ];

  void startOver() {
    _step = 0;
    notifyListeners();
  }

  void saveStep(String value) {
    nextStep();
  }

  void nextStep() {
    _step++;
    notifyListeners();
  }

  void previousStep() {
    _step--;
    notifyListeners();
  }
}

class Step {
  String field;
  TextEditingController controller;
  String hint;
  StepType type;

  Step({this.field, this.controller, this.hint, this.type});
}
Rody Davis
  • 1,825
  • 13
  • 20