【问题标题】:Flutter, Inconsistent Widget build颤振,不一致的小部件构建
【发布时间】:2019-12-27 07:33:12
【问题描述】:

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

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(...........);
 }

我有这样的根小部件:

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 的状态以避免从头开始重新创建它的最佳方法是什么?

【问题讨论】:

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


    【解决方案1】:

    是的,这是正常行为。您必须使用 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))),
          ),
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-30
      • 2019-01-14
      • 2023-03-07
      • 1970-01-01
      • 2018-10-24
      • 2020-09-07
      • 2021-11-25
      • 2018-05-10
      相关资源
      最近更新 更多