I'm trying to build a parent widget that has a button, when clicked, it displays another widget with some text and a drop-down list. When the drop-down selection is changed, the text should change accordingly. I've included below a simplified code of what I'm trying to achieve which doesn't work. The state lifting up concept is something confusing for me as a newcomer to Flutter
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String text = "Empty";
void addWidget() {
setState(() {
widList.clear();
widList.add(MidWidget(
text: text,
setValue: selectValue,
));
});
}
void selectValue(String value) {
setState(() {
text = value;
});
}
List<Widget> widList = [];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(children: [
ElevatedButton(onPressed: addWidget, child: const Text("Add Widget")),
Column(
children: widList,
)
]),
),
);
}
}
class MidWidget extends StatelessWidget {
const MidWidget({super.key, required this.text, required this.setValue});
final String text;
final Function setValue;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(text),
LowestWidget(
dropDownValue: "First",
setValue: setValue,
),
],
);
}
}
////////////////////
///////////////////
///
class LowestWidget extends StatelessWidget {
LowestWidget(
{super.key, required this.dropDownValue, required this.setValue});
final List<String> items = ["First", "Second"];
final String dropDownValue;
final Function setValue;
@override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropDownValue,
icon: const Icon(Icons.arrow_downward),
onChanged: (String? value) {
setValue(value);
},
items: items.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}

First of all, both
MidWidgetandLowestWidgetneed to be converted toStatefulWidgetbecause we need state changes inside those widgets too.Secondly,
selectValuefunction should be in theMidWidget, not in the parent widget, because it attempts to change the state oftextthat has already been passed onto theMidWidgetwith its original value at the time of its instantiation. Any change intextviasetStateis not going to affect its value inMidWidgetanymore.Thirdly, I've introduced
_valuevariable in bothMidWidgetandLowestWidgetthat takes its initial value from the respective parent widgets ininitStateand then gets value changes viasetStatethat are then used to be displayed inTextwidget inMidWidgetandDropdownButtonwidget inLowestWidget.Following is the revised code that is working as per your requirements. I've commented out the deletions so that you could relate it with the original code.
Hope it helps!