Flutter,不一致的小部件构建

时间:2019-08-22 02:59:25

标签: flutter dart flutter-layout flutter-navigation statefulwidget

所以我有一个这样的表单屏幕(带有自己的支架):

class InputForm extends StatelessWidget {
  final Receipt initialReceipt;

  InputForm({this.initialReceipt}){
    print("InputForm() called");
  }

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      bloc: InputFormBloc(),
      child: InputFromWidget(initialReceipt: initialReceipt),
    );
  }
}

class InputFromWidget extends StatelessWidget {
  final Receipt initialReceipt;

  InputFromWidget({this.initialReceipt}){
    print("InputFromWidget() called");
  }
 @override
 Widget build(BuildContext context) {
     return Scaffold(...........);
 }

我有这样的根Widget:

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

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      child: MaterialApp(
        title: 'Takefin',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          primarySwatch: Colors.purple,
        ),
        //home: InputForm(),
        home: DashboardWidget(),
      ),
      bloc: TakeFinBloc(),
    );
  }
}

它何时按计划运作?

我设置的时间

home: InputForm()

即使我单击任意位置并且UI发生更改(下拉或单击输入字段)

print("InputForm() called");        
print("InputFromWidget() called");

仅被调用一次。

但是当我设置

home: DashboardWidget(),

然后在DashboardWidget上单击一个按钮,我这样做是:

MaterialPageRoute(builder: (context) => InputForm()) 

然后,当我在InputForm屏幕上的某个位置单击以更改小部件时(例如,展开下拉菜单,在字段中键入内容)

print("InputForm() called");
print("InputFromWidget() called");
每次点击

都会导致布局发生变化。

这是正常行为吗?

如果是,最好的存储InputFromWidget状态以避免从头开始创建状态的方法是什么?

1 个答案:

答案 0 :(得分:0)

是的,这是正常现象。您必须使用StatefulWidget存储InputFromWidget的状态,以避免从头开始创建它。 从下面的注册屏幕示例中,您可以了解如何管理InputFromWidget的状态。

sign_up_screen.dart

class SignUpScreen extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _SignUpScreenState();
  }
}

class _SignUpScreenState extends State<SignUpScreen> {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text("SignUp"),
      ),
      body: Container(
        padding: EdgeInsets.all(15.0),
        color: Colors.white,
        child: SingleChildScrollView(
          child: Column(
            children: <Widget>[
              SignUpFormWidget(),
              SocialLoginWidget(),
            ],
          ),
        ),
      ),
    );
  }
}

sing_up_form_widget.dart

class SignUpFormWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _SignUpFormWidgetState();
  }
}

class _SignUpFormWidgetState extends State<SignUpFormWidget> {
  final _formKey = GlobalKey<FormState>();

  var _userNameController = TextEditingController(text: "");
  var _userEmailController = TextEditingController(text: "");
  var _userPasswordController = TextEditingController(text: "");

  var _emailFocusNode = FocusNode();
  var _passwordFocusNode = FocusNode();
  bool _isPasswordVisible = true;
  bool _autoValidate = false;

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      autovalidate: _autoValidate,
      child: Column(
        children: <Widget>[
          _buildUserNameField(context),
          _buildEmailField(context),
          _buildPasswordField(context),
          _buildTermsWidget(context),
          _buildSignUpButton(context),
        ],
      ),
    );
  }

  Widget _buildUserNameField(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 5),
      child: TextFormField(
        controller: _userNameController,
        keyboardType: TextInputType.text,
        textInputAction: TextInputAction.next,
        onFieldSubmitted: (_) {
          FocusScope.of(context).requestFocus(_emailFocusNode);
        },
        validator: (value) => _userNameValidation(value),
        decoration: CommonStyles.textFormFieldStyle("User Name", ""),
      ),
    );
  }

  String _userNameValidation(String value) {
    if (value.isEmpty) {
      return "Please enter valid user name";
    } else {
      return null;
    }
  }

  Widget _buildEmailField(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 5),
      child: TextFormField(
        controller: _userEmailController,
        keyboardType: TextInputType.emailAddress,
        textInputAction: TextInputAction.next,
        onFieldSubmitted: (_) {
          FocusScope.of(context).requestFocus(_passwordFocusNode);
        },
        validator: (value) => _emailValidation(value),
        decoration: CommonStyles.textFormFieldStyle("Email", ""),
      ),
    );
  }

  String _emailValidation(String value) {
    bool emailValid =
        RegExp(r"^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(value);
    if (!emailValid) {
      return "Enter valid email address";
    } else {
      return null;
    }
  }

  Widget _buildPasswordField(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 5),
      child: TextFormField(
        controller: _userPasswordController,
        keyboardType: TextInputType.text,
        textInputAction: TextInputAction.next,
        onFieldSubmitted: (_) {
          FocusScope.of(context).requestFocus(_emailFocusNode);
        },
        validator: (value) => _userNameValidation(value),
        obscureText: _isPasswordVisible,
        decoration: InputDecoration(
          labelText: "Password",
          hintText: "",
          labelStyle: TextStyle(color: Colors.black),
          alignLabelWithHint: true,
          contentPadding: EdgeInsets.symmetric(vertical: 5),
          suffixIcon: IconButton(
              icon: Icon(
                _isPasswordVisible ? Icons.visibility_off : Icons.visibility,
                color: Colors.black,
              ),
              onPressed: () {
                setState(() {
                  _isPasswordVisible = !_isPasswordVisible;
                });
              }),
          enabledBorder: UnderlineInputBorder(
            borderSide: BorderSide(
              color: Colors.black,
            ),
          ),
          focusedBorder: UnderlineInputBorder(
            borderSide: BorderSide(color: Colors.black, width: 2),
          ),
        ),
      ),
    );
  }

  Widget _buildTermsWidget(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
      child: GestureDetector(
        onTap: (){_openTermsInWeb();},
        child: RichText(
          text: new TextSpan(
            style: new TextStyle(
              fontSize: 14.0,
              color: Colors.black,
            ),
            children: <TextSpan>[
              TextSpan(
                text: 'Terms of use',
                style:
                    TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
              ),
              TextSpan(
                text: ' and ',
                style: TextStyle(
                    color: Colors.black54, fontWeight: FontWeight.w500),
              ),
              TextSpan(
                text: 'Privacy policy',
                style:
                    TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildSignUpButton(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 15.0),
      width: double.infinity,
      child: RaisedButton(
        color: Colors.black,
        onPressed: () {
          _signUpProcess(context);
        },
        child: Text(
          "Sign Up",
          style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800),
        ),
      ),
    );
  }

  void _signUpProcess(BuildContext context) {
    var validate = _formKey.currentState.validate();

    if (validate) {
      showDialog(
          context: context,
          builder: (BuildContext ctx) {
            return AlertDialog(
              title: Text("Sign Up Success."),
              actions: <Widget>[
                FlatButton(
                    onPressed: () {
                      _clearAllFields();
                      Navigator.of(context).pop();
                    },
                    child: Text("Close"))
              ],
            );
          });
    } else {
      setState(() {
        _autoValidate = true;
      });
    }
  }

  void _clearAllFields() {
    setState(() {
      _userNameController = TextEditingController(text: "");
      _userEmailController = TextEditingController(text: "");
      _userPasswordController = TextEditingController(text: "");
    });
  }

  _openTermsInWeb( ) async {
    const url = 'https://www.solutionanalysts.com/terms-use/';
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }
}

social_login_widget.dart

class SocialLoginWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Column(
      children: <Widget>[
        _buildSocialLoginTextWidget(context),
        Row(
          children: <Widget>[
            __buildFacebookButtonWidget(context),
            __buildTwitterButtonWidget(context)
          ],
        )
      ],
    );
  }

  Widget _buildSocialLoginTextWidget(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
      child: RichText(
        text: new TextSpan(
          style: new TextStyle(
            fontSize: 14.0,
            color: Colors.black,
          ),
          children: <TextSpan>[
            TextSpan(
              text: 'Or sign up with social account',
              style:
                  TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
            ),
          ],
        ),
      ),
    );
  }

  Widget __buildTwitterButtonWidget(BuildContext context) {
    return Expanded(
      flex: 1,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8.0),
        child: RaisedButton(
            color: Color.fromRGBO(29, 161, 242, 1.0),
            child: Image.asset(
              "assets/images/ic_twitter.png",
              width: 25,
              height: 25,
            ),
            onPressed: () {},
            shape: RoundedRectangleBorder(
                borderRadius: new BorderRadius.circular(30.0))),
      ),
    );
  }

  Widget __buildFacebookButtonWidget(BuildContext context) {
    return Expanded(
      flex: 1,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8.0),
        child: RaisedButton(
            color: Color.fromRGBO(42, 82, 151, 1.0),
            child: Image.asset(
              "assets/images/ic_fb.png",
              width: 35,
              height: 35,
            ),
            onPressed: () {},
            shape: RoundedRectangleBorder(
                borderRadius: new BorderRadius.circular(30.0))),
      ),
    );
  }
}