dart 为什么一个子底部工作表的状态改变会触发父部件的重建?

qlckcl4x  于 2023-06-19  发布在  其他
关注(0)|答案(2)|浏览(117)

我有一个脚手架屏幕(ListsScreen)。它有一个Button(AddNewListButton),用于打开Modal Bottom Sheet(ListScreenBottomSheetWidget)。底部工作表具有TextField(ListTitleInputTextFieldWidget)。
当我点击TextField打开键盘时,父屏幕会自行重建,当然,所有的子部件也会重建。
为什么会这样?我的印象是,国家的变化只会重建自己或他们的孩子,而不是他们的父母。我还添加了const构造函数,几乎所有地方,以避免重建,但这仍然发生。
父列表屏幕:

class ListsScreen extends StatelessWidget {
  const ListsScreen();
  static const routeName = '/lists-screen';

  @override
  Widget build(BuildContext context) {
    final user = Provider.of<AuthProvider>(context, listen: false).getUser;
    print('stateless rebuilding');
    return Scaffold(
      appBar: AppBar(
        centerTitle: false,
        title: Text(
          '${user['name']}\'s Lists',
          style: TextStyle(
            color: Theme.of(context).primaryColorLight,
          ),
        ),
        actions: <Widget>[
          const SignOutButton(),
        ],
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(20.0),
          child: Column(
            children: <Widget>[
              SizeConfig.smallDevice
                  ? const SizedBox(
                      height: 30,
                    )
                  : const SizedBox(
                      height: 40,
                    ),
              SizeConfig.smallDevice
                  ? Text(
                      'Welcome to TODOS',
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        fontSize: 20,
                        color: Colors.grey[700],
                      ),
                    )
                  : Text(
                      'Welcome to TODOS',
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        fontSize: 25,
                        color: Colors.grey[700],
                      ),
                    ),
              SizeConfig.smallDevice
                  ? const SizedBox(
                      height: 30,
                    )
                  : const SizedBox(
                      height: 40,
                    ),
              const AddNewListButton(),
              SizeConfig.smallDevice
                  ? const SizedBox(
                      height: 30,
                    )
                  : const SizedBox(
                      height: 40,
                    ),
              const UserListsListViewWidget(),
            ],
          ),
        ),
      ),
    );
  }
}

class SignOutButton extends StatelessWidget {
  const SignOutButton();

  Future<void> _submitRequest(BuildContext context) async {
    _showLoadingAlert(context);

    try {
      await Provider.of<AuthProvider>(context, listen: false)
          .submitLogOutRequest();
      Navigator.of(context).pop();
      Navigator.of(context).pushReplacementNamed(LoginScreen.routeName);
    } on HttpExceptions catch (error) {
      Navigator.of(context).pop();
      _showErrorDialogue(error.getErrorList, context);
    }
  }

  void _showErrorDialogue(List<dynamic> errorMessages, BuildContext context) {
    showDialog(
      context: context,
      builder: (ctx) => Dialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(20.0),
        ),
        child: ErrorListWidget(errorMessages: errorMessages),
      ),
    );
  }

  void _showLoadingAlert(BuildContext context) {
    showDialog(
      context: context,
      builder: (ctx) => const LoadingWidget(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return FlatButton(
      onPressed: () => _submitRequest(context),
      child: Row(
        children: <Widget>[
          Text(
            'Sign Out',
            style: TextStyle(
              color: Theme.of(context).primaryColorLight,
            ),
          ),
          Icon(
            Icons.exit_to_app,
            color: Theme.of(context).primaryColorLight,
          ),
        ],
      ),
    );
  }
}

class AddNewListButton extends StatelessWidget {
  const AddNewListButton();

  void _modalBottomSheetMenu(BuildContext context) {
    showModalBottomSheet(
      context: context,
      backgroundColor: Colors.transparent,
      isScrollControlled: true,
      builder: (builder) {
        return const ListScreenBottomSheetWidget();
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(5),
      ),
      elevation: 10,
      color: Theme.of(context).primaryColor,
      onPressed: () => _modalBottomSheetMenu(context),
      child: Text(
        '+ Add List',
        style: TextStyle(
          color: Colors.white,
          fontSize: SizeConfig.smallDevice ? 10 : 15,
        ),
      ),
    );
  }
}

模态底表:

import 'package:flutter/material.dart';
import 'package:todo_spicotech/helpers/size_config.dart';

class ListScreenBottomSheetWidget extends StatelessWidget {
  const ListScreenBottomSheetWidget();
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        FocusScopeNode currentFocus = FocusScope.of(context);
        if (!currentFocus.hasPrimaryFocus) {
          currentFocus.unfocus();
        }
        currentFocus.unfocus();
      },
      child: Container(
        margin: const EdgeInsets.all(20.0),
        padding: EdgeInsets.only(
          bottom: MediaQuery.of(context).viewInsets.bottom,
        ),
        child: Material(
          borderRadius: BorderRadius.all(Radius.circular(15)),
          elevation: 10,
          child: Padding(
            padding: const EdgeInsets.all(20.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[

                SizeConfig.smallDevice
                    ? Text(
                        'Create a new List',
                        textAlign: TextAlign.center,
                        style: TextStyle(
                          fontSize: 20,
                          color: Colors.grey[700],
                        ),
                      )
                    : Text(
                        'Create a new List',
                        textAlign: TextAlign.center,
                        style: TextStyle(
                          fontSize: 25,
                          color: Colors.grey[700],
                        ),
                      ),
                SizeConfig.smallDevice
                    ? const SizedBox(
                  height: 20,
                )
                    : const SizedBox(
                  height: 30,
                ),
                const ListTitleInputTextFieldWidget(),
                SizeConfig.smallDevice
                    ? const SizedBox(
                  height: 20,
                )
                    : const SizedBox(
                  height: 30,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    InkWell(
                      borderRadius: BorderRadius.circular(5),
                      onTap: () {
                        Navigator.of(context).pop();
                      },
                      child: Ink(
                        padding: EdgeInsets.all(10),
                        child: const Text('CANCEL'),
                      ),
                    ),
                    RaisedButton(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(5),
                      ),
                      elevation: 10,
                      color: Theme.of(context).primaryColor,
                      onPressed: () {},
                      child: Text(
                        'Create',
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: SizeConfig.smallDevice ? 10 : 15,
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class ListTitleInputTextFieldWidget extends StatefulWidget {
  const ListTitleInputTextFieldWidget();
  @override
  _ListTitleInputTextFieldWidgetState createState() => _ListTitleInputTextFieldWidgetState();
}

class _ListTitleInputTextFieldWidgetState extends State<ListTitleInputTextFieldWidget> {
  @override
  Widget build(BuildContext context) {
    return TextFormField(
      decoration: const InputDecoration(
        enabledBorder: OutlineInputBorder(
          borderSide: BorderSide(
            color: Colors.lightBlue,
          ),
        ),
        focusedBorder: OutlineInputBorder(
          borderSide: BorderSide(
            color: Colors.lightBlue,
          ),
        ),
        labelText: 'List Title',
        contentPadding: EdgeInsets.all(10),
      ),
    );
  }
}
pbpqsu0x

pbpqsu0x1#

当你调用showModalBottomSheet时,它实际上在里面使用了Navigator

return Navigator.of(context, rootNavigator: useRootNavigator).push(_ModalBottomSheetRoute<T>(
builder: builder,

showModalBottomSheet源码https://github.com/flutter/flutter/blob/17079f26b54c8517678699a0cefe5f7bfec67b3f/packages/flutter/lib/src/material/bottom_sheet.dart#L635
Flutter teams回复问题Pages on Navigator stack rebuild when a new page is pushed https://github.com/flutter/flutter/issues/11655#issuecomment-348287396
这是按预期工作。一般来说,您应该假设所有的小部件都可以在任何时候重新构建,而它们并不只是一种优化。
特别是,路由将重新构建,因为它们的导航器状态已更改,因此它们可能需要更新它们如何拉回按钮等。

6uxekuva

6uxekuva2#

如果你想避免在使用模态底页时重新构建页面,你需要创建'context' showModalBottomSheet( context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(16.0), ), ), backgroundColor: kTransparentColor, builder: (BuildContext context) => modalCheckKtp(**context**));
在modalCheckKtp中,需要参数BuildContext

相关问题