内容
親画面からモーダルボトムシートを呼び出し後、親画面のState更新処理をすることで子要素であるモーダルボトムシート内も一緒に再描画されると思っていた。
修正後
親画面とモーダルボトムシート両方にStateを持たせて更新する必要があった。
サンプルコードだがこれで解決できるらしい。
親画面側
class ParentScreenState {
const ParentScreenState({
required this.checkboxValues,
required this.selectedItems,
});
final List<bool> checkboxValues;
final List<String> selectedItems;
ParentScreenState copyWith({
List<bool>? checkboxValues,
List<String>? selectedItems,
}) {
return ParentScreenState(
checkboxValues: checkboxValues ?? this.checkboxValues,
selectedItems: selectedItems ?? this.selectedItems,
);
}
}
class ParentScreenVM extends StateNotifier<ParentScreenState> {
ParentScreenVM()
: super(
ParentScreenState(
checkboxValues: List.generate(4, (_) => false),
selectedItems: [],
),
);
void toggleCheckbox(int index) {
final newCheckboxValues = [...state.checkboxValues];
newCheckboxValues[index] = !newCheckboxValues[index];
final newSelectedItems = <String>[];
for (var i = 0; i < newCheckboxValues.length; i++) {
if (newCheckboxValues[i]) {
newSelectedItems.add('チェックボックス ${i + 1}');
}
}
state = state.copyWith(
checkboxValues: newCheckboxValues,
selectedItems: newSelectedItems,
);
}
void updateFromBottomSheet(List<bool> newCheckboxValues) {
final newSelectedItems = <String>[];
for (var i = 0; i < newCheckboxValues.length; i++) {
if (newCheckboxValues[i]) {
newSelectedItems.add('チェックボックス ${i + 1}');
}
}
state = state.copyWith(checkboxValues: newCheckboxValues, selectedItems: newSelectedItems);
}
}
class ParentScreen extends StatelessWidget {
const ParentScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return StateNotifierProvider<ParentScreenVM, ParentScreenState>(
create: (_) => ParentScreenVM(),
builder: (context, _) {
return Scaffold(
appBar: AppBar(title: const Text('親画面')),
body: _body(context),
);
},
);
}
Widget _body(BuildContext ctx) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_selectedItemsSection(ctx),
const SizedBox(height: 16),
_checkboxList(ctx),
const SizedBox(height: 16),
_bottomSheetButton(ctx),
],
),
);
}
Widget _selectedItemsSection(BuildContext ctx) {
final selectedItems = ctx.select<ParentScreenState, List<String>>(
(state) => state.selectedItems,
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'選択された項目:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
if (selectedItems.isEmpty)
const Text('選択されている項目はありません')
else
...selectedItems.map(
(item) => Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
'・$item',
style: const TextStyle(fontSize: 14),
),
),
),
],
);
}
Widget _checkboxList(BuildContext ctx) {
final checkboxValues = ctx.select<ParentScreenState, List<bool>>(
(state) => state.checkboxValues,
);
return Column(
children: [
const Divider(),
const SizedBox(height: 16),
...List.generate(
4,
(index) => CheckboxListTile(
value: checkboxValues[index],
onChanged: (_) => ctx.read<ParentScreenVM>().toggleCheckbox(index),
title: Text('チェックボックス ${index + 1}'),
),
),
],
);
}
Widget _bottomSheetButton(BuildContext ctx) {
final checkboxValues = ctx.select<ParentScreenState, List<bool>>(
(state) => state.checkboxValues,
);
return Center(
child: ElevatedButton(
onPressed: () {
showModalBottomSheet(
context: ctx,
builder: (context) => CustomModalBottomSheet(
initialCheckboxValues: checkboxValues,
onCheckboxesUpdated: (newValues) {
ctx.read<ParentScreenVM>().updateFromBottomSheet(newValues);
},
),
);
},
child: const Text('ボトムシート表示'),
),
);
}
}
モーダルボトムシート側
class ModalBottomSheetState {
const ModalBottomSheetState({
required this.checkboxValues,
});
final List<bool> checkboxValues;
ModalBottomSheetState copyWith({
List<bool>? checkboxValues,
}) {
return ModalBottomSheetState(
checkboxValues: checkboxValues ?? this.checkboxValues,
);
}
}
class ModalBottomSheetVM extends StateNotifier<ModalBottomSheetState> {
ModalBottomSheetVM(List<bool> initialValues)
: super(
ModalBottomSheetState(
checkboxValues: initialValues,
),
);
void toggleCheckbox(int index) {
final newCheckboxValues = [...state.checkboxValues];
newCheckboxValues[index] = !newCheckboxValues[index];
state = state.copyWith(checkboxValues: newCheckboxValues);
}
}
class CustomModalBottomSheet extends StatelessWidget {
const CustomModalBottomSheet({
required this.initialCheckboxValues,
required this.onCheckboxesUpdated,
Key? key,
}) : super(key: key);
final List<bool> initialCheckboxValues;
final void Function(List<bool>) onCheckboxesUpdated;
@override
Widget build(BuildContext context) {
return StateNotifierProvider<ModalBottomSheetVM, ModalBottomSheetState>(
create: (_) => ModalBottomSheetVM(initialCheckboxValues),
builder: (context, _) {
return Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('ボトムシート', style: TextStyle(fontSize: 18)),
_checkboxList(context),
],
),
);
},
);
}
Widget _checkboxList(BuildContext ctx) {
final checkboxValues = ctx.select<ModalBottomSheetState, List<bool>>(
(state) => state.checkboxValues,
);
return Column(
children: List.generate(
4,
(index) => CheckboxListTile(
value: checkboxValues[index],
onChanged: (_) {
ctx.read<ModalBottomSheetVM>().toggleCheckbox(index);
onCheckboxesUpdated(ctx.read<ModalBottomSheetState>().checkboxValues);
},
title: Text('チェックボックス ${index + 1}'),
),
),
);
}
}