我有一个脚手架屏幕(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),
),
);
}
}
答案 0 :(得分: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的答复
这按预期工作。通常,您应该假定所有小部件都可以随时重建,而大多数情况下它们并不是最优化的。
特别是,路线将重建,因为其导航器状态已更改,因此它们可能需要更新其回退按钮等的方式。