为什么子底板的状态变化会触发父窗口小部件的重建?

问题描述 投票:0回答:1

我有一个脚手架屏幕(ListsScreen)。它具有一个Button(AddNewListButton),可以打开模态底部工作表(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),
      ),
    );
  }
}

flutter dart bottom-sheet stateless stateful
1个回答
0
投票

[当您呼叫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团队对问题Pages on Navigator stack rebuild when a new page is pushed https://github.com/flutter/flutter/issues/11655#issuecomment-348287396的答复>

这已按预期工作。通常,您应该假设所有小部件都可以随时重建,而大多数情况下它们并不是最优化的。

特别是,由于导航器的状态已更改,因此路线将重建,因此它们可能需要更新回退按钮等的方式。

© www.soinside.com 2019 - 2024. All rights reserved.