Error Nested ListView.builder() with GetX - flutter

I'm trying to make an example with GetX where I have the following structure:
A HomePage with a list of Widget1, each Widget1 has a list of Widget2.
I'm having two problems:
The first one is that I can only add one Widget1, when I go to add the second one, it shows the following error:
The following assertion was thrown building Widget1(dirty):
setState() or markNeedsBuild() called during build.
This GetX widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: GetX
The second problem is that when I press the button to add new Widget2, inside Widget1, nothing happens. I put a print in the list of Widget2, and it is being filled, but visually it doesn't show.
I/flutter (27561): list: 1
I/flutter (27561): list: 2
I/flutter (27561): list: 3
I/flutter (27561): list: 4
HomePage:
class HomePage extends GetView<HomeController> {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
final homeController = Get.put(HomeController());
final widget1Controller = Get.put(Widget1Controller());
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
final mykey = GlobalKey();
controller.addWidget1(Widget1(mykey: mykey));
},
),
body: GetX<HomeController>(
builder: (_) {
return ListView.builder(
itemBuilder: (context, index) {
return controller.getWidget1[index];
},
itemCount: controller.getWidget1.length,
);
},
),
);
}
}
HomePage Controller:
class HomeController extends GetxController {
final RxList<Widget1> _widget1List = <Widget1>[].obs;
void addWidget1(Widget1 newWidget1) {
_widget1List.add(newWidget1);
}
List<Widget1> get getWidget1 {
return _widget1List;
}
}
Widget1:
class Widget1 extends GetView<Widget1Controller> {
final Key mykey;
const Widget1({required this.mykey, super.key});
#override
Widget build(BuildContext context) {
controller.setListWidget2(mykey);
return GetX<Widget1Controller>(
builder: (_) {
return ExpansionTile(
title: Container(
color: Colors.blue,
width: 50.0,
height: 50.0,
),
children: [
ListView.builder(
itemBuilder: (context, index) {
return controller.getListWidget2(mykey)[index];
},
itemCount: controller.getListWidget2(mykey).length,
shrinkWrap: true,
),
IconButton(
onPressed: () {
controller.addWidget2(const Widget2(), mykey);
debugPrint("list: ${controller.getListWidget2(mykey).length}");
},
icon: const Icon(Icons.add),
)
],
);
},
);
}
}
Widget1 Controller:
class Widget1Controller extends GetxController {
final RxMap<Key, List<Widget2>> _mapListWidget2 = <Key, List<Widget2>>{}.obs;
void setListWidget2(Key mykey) {
_mapListWidget2[mykey] = <Widget2>[];
}
void addWidget2(Widget2 newWidget2, Key mykey) {
_mapListWidget2[mykey]!.add(newWidget2);
}
List<Widget2> getListWidget2(Key key) {
if (_mapListWidget2[key] != null) {
return _mapListWidget2[key]!;
} else {
return <Widget2>[];
}
}
}
Widget2:
class Widget2 extends StatelessWidget {
const Widget2({super.key});
#override
Widget build(BuildContext context) {
return Container(
height: 300.0,
width: 300.0,
color: Colors.red,
);
}
}
Main:
void main() {
runApp(
const GetMaterialApp(
home: HomePage(),
),
);
}
I am really lost, how can i solve these two problems?

You were facing these issues because some of you have not implemented some of the functionality properly as GetX suggests.
Some of the issues were like putting the controller from initial bindings instead of directly putting it inside the build method.
Here's the working code.
Main app and HomePage
void main() {
runApp(
GetMaterialApp(
initialBinding: InitalBinding(),
home: const HomePage(),
),
);
}
class HomePage extends GetView<HomeController> {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
var mykey = UniqueKey();
controller.addWidget1(Widget1(mykey: mykey));
},
),
body: GetX<HomeController>(
builder: (logic) {
return ListView.builder(
itemBuilder: (context, index) {
return logic.getWidget1[index];
},
itemCount: logic.getWidget1.length,
);
},
),
);
}
}
HomeController (It remains the same)
class HomeController extends GetxController {
final RxList<Widget1> _widget1List = <Widget1>[].obs;
void addWidget1(Widget1 newWidget1) {
_widget1List.add(newWidget1);
}
List<Widget1> get getWidget1 {
return _widget1List;
}
}
Widget - 1
class Widget1 extends GetView<Widget1Controller> {
final Key mykey;
const Widget1({required this.mykey, super.key});
#override
Widget build(BuildContext context) {
return GetBuilder<Widget1Controller>(
builder: (controller) {
return ExpansionTile(
title: Container(
color: Colors.blue,
width: 50.0,
height: 50.0,
),
children: [
ListView.builder(
itemBuilder: (context, index) {
return controller.getListWidget2(mykey)[index];
},
itemCount: controller.getListWidget2(mykey).length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
),
IconButton(
onPressed: () {
if (controller.mapListWidget2.containsKey(mykey)) {
controller.addWidget2(const Widget2(), mykey);
controller.update();
} else {
controller.setListWidget2(mykey);
controller.addWidget2(const Widget2(), mykey);
controller.update();
}
debugPrint("list: ${controller.getListWidget2(mykey).length}");
},
icon: const Icon(Icons.add),
)
],
);
},
);
}
}
Widget - 1 Controller
class Widget1Controller extends GetxController {
final RxMap<Key, List<Widget2>> mapListWidget2 = <Key, List<Widget2>>{}.obs;
void setListWidget2(Key mykey) {
mapListWidget2[mykey] = <Widget2>[];
}
void addWidget2(Widget2 newWidget2, Key mykey) {
mapListWidget2[mykey]!.add(newWidget2);
}
List<Widget2> getListWidget2(Key key) {
if (mapListWidget2[key] != null) {
return mapListWidget2[key]!;
} else {
return <Widget2>[];
}
}
}
Widget - 2 remains the same.
I have put dependency injection in another file called bindings.
Initial Bindings
class InitalBinding implements Bindings {
#override
void dependencies() {
Get.put<HomeController>(HomeController(), permanent: true);
Get.put<Widget1Controller>(Widget1Controller(), permanent: true);
}
}

Related

Persisting google Banner Ad on bottom navigation bar in all Screens

Am new to flutter. I am creating a flutter app involving google mobile ads. I want to implement a banner AD on the bottom navigation bar of every screen. Is there a way to implement the banner ad once on one screen (on the bottom nav bar) and persist it throughout all screens than creating instances of a banner Ad on each screen and creating a bottom navigation bar? I Am using getx for routes and here's one of my screens where I implemented the banner ad.
class ProgrammeScreen extends StatefulWidget {
const ProgrammeScreen({Key? key}) : super(key: key);
static const routeName = '/ProgrammeScreen';
#override
State<ProgrammeScreen> createState() => _ProgrammeScreenState();
}
class _ProgrammeScreenState extends State<ProgrammeScreen> {
late BannerAd _bottomBannerAd1;
bool _isBottomBannerLoaded = false;
void createBottomBannerAd() {
_bottomBannerAd1 = BannerAd(
listener: BannerAdListener(onAdLoaded: (_) {
setState(() {
_isBottomBannerLoaded = true;
});
}, onAdFailedToLoad: (ad, error) {
ad.dispose(); }),
adUnitId: bannerKey,
size: AdSize.banner,
request: const AdRequest());
_bottomBannerAd1.load();
}
#override
void initState() {
createBottomBannerAd();
super.initState();
}
#override
void dispose() {
_bottomBannerAd1.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Programme'),
backgroundColor: appBarColor(context),
),
bottomNavigationBar: _isBottomBannerLoaded ? SizedBox(
height: _bottomBannerAd1.size.height.toDouble(),
width: _bottomBannerAd1.size.width.toDouble(),
child: AdWidget(ad: _bottomBannerAd1),
) : const SizedBox(),
body: SizedBox(
width: UIParameters.getWidth(context),
height: UIParameters.getHeight(context),
child: Padding(
padding: const EdgeInsets.all(kMobileScreenPadding),
child: Column(
children: [
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: estudieeFR.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
} else if (snapshot.hasData) {
return ListView.separated(
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (BuildContext context, int index) {
final data = snapshot.data!.docs[index];
return ContentCard(
title: data['name'],
icon: Icons.arrow_forward_ios,
onPressed: () => Get.to(
() => YearScreen(
programId: snapshot.data!.docs[index].id),
),
);
},
separatorBuilder: (BuildContext context, int index) {
return const Divider();
},
);
} else {
return Indicators.circularIndicator;
}
}),
)
],
),
),
),
);
}
}
Heres my main.dart file with the root widget(MyApp)
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
#override
Widget build(BuildContext context) {
return GetMaterialApp(
navigatorKey: navigatorKey,
defaultTransition: Transition.rightToLeftWithFade,
initialRoute: '/',
getPages: AppRoutes.pages(),
debugShowCheckedModeBanner: false,
);
}
}
I don't want to copy the same code in all the stateful widgets but rather implement a single bottom navigation banner Ad that will persist all screens. Is there any way to achieve this?
If you do not want to write the same code in all screens, then you could make modifications in the builder function of MaterialApp to include your banner ad there as:
MaterialApp(
builder: (BuildContext context, Widget? child) {
return Column(
children: [
Expanded(child: child!),
// your banner widget here,
],
);
},
home: HomeScreen(),
);
Create another dart file and add this code
import 'package:google_mobile_ads/google_mobile_ads.dart';
late BannerAd bannerAd;
loadBanner() {
  bannerAd = BannerAd(
    adUnitId: BannerAd.testAdUnitId,
    size: AdSize.fullBanner,
    request: const AdRequest(),
    listener: BannerAdListener(
      // Called when an ad is successfully received.
      onAdLoaded: (Ad ad) => print('Ad loaded.'),
      // Called when an ad request failed.
      onAdFailedToLoad: (Ad ad, LoadAdError error) {
        // Dispose the ad here to free resources.
        ad.dispose();
        print('Ad failed to load: $error');
      },
      // Called when an ad opens an overlay that covers the screen.
      onAdOpened: (Ad ad) => print('Ad opened.'),
      // Called when an ad removes an overlay that covers the screen.
      onAdClosed: (Ad ad) => print('Ad closed.'),
      // Called when an impression occurs on the ad.
      onAdImpression: (Ad ad) => print('Ad impression.'),
    ),
  )..load();
}
Call the loadBanner once and this bannerAd is available in all classes since it's defined in the root

ValueNotifier is not returning that data which is being initialized Flutter

I am using ValueListenableBuilder to update my UI based on the data provided to it. I am initializing the ValueNotifier with value. But when I tried to read that value it returns nothing.
This is my Notifier class code:
class AppValueNotifier
{
ValueNotifier<List<Food>> valueNotifier = ValueNotifier([]);
void updateDealsList(List<Food> list)
{
valueNotifier.value=list;
print('DEAL LIST IN CLASS: ${ valueNotifier.value}');
}
List<Food> getDealList()
{
return valueNotifier.value;
}
}
In a separate widget I am initializing the value like this:
class HomeWidgetState extends State<HomeWidget> {
AppValueNotifier appValueNotifier = AppValueNotifier();
.
.
.
assignList(List<Food> dealList)
{
appValueNotifier.updateDealsList(dealList);
}
..
..
.
}
Now in another widget class I am building my UI with this data like this:
AppValueNotifier appValueNotifier = AppValueNotifier();
Widget buildList()
{
return ValueListenableBuilder(
valueListenable: appValueNotifier.valueNotifier,
builder: (context, List<Food> value, widget) {
print(
'DEAL LIST: ${appValueNotifier.getDealList()}');
return DealsWidget(
key: menuItemsKey,
updateList: (oldIndex, newIndex, newList) {},
currentMenu: value,
menuItemNodes: [],
changeCellColor: (color, catid) {},
);
},
);
}
But it is returning empty list instead. Not that list which is being initialized at the start.
Anyone help me what is the issue here:
Thanks in advance
You should be able to initialize your ValueNotifier list either in the constructor or based on an action (i.e. a button click, as shown below). Notice how I'm providing the AppValueNotifier service using the Provider pattern, and one widget triggers the action while a separate widget listens to the changes being made.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
Provider(
create: (context) => AppValueNotifier(),
child: MyApp()
)
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(
children: [
TriggerWidget(),
Expanded(
child: MyWidget(),
)
]
),
),
);
}
}
class Food {
final String name;
Food({ required this.name });
}
class AppValueNotifier
{
ValueNotifier<List<Food>> valueNotifier = ValueNotifier([]);
void updateDealsList(List<Food> list)
{
valueNotifier.value = list;
print('DEAL LIST IN CLASS: ${ valueNotifier.value}');
}
List<Food> getDealList()
{
return valueNotifier.value;
}
}
class TriggerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
AppValueNotifier appValueNotifier = Provider.of<AppValueNotifier>(context, listen: false);
return TextButton(
child: const Text('Add Items!'),
onPressed: () {
appValueNotifier.updateDealsList([
Food(name: 'Food 1'),
Food(name: 'Food 2'),
]);
},
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
AppValueNotifier appValueNotifier = Provider.of<AppValueNotifier>(context, listen: false);
return ValueListenableBuilder(
valueListenable: appValueNotifier.valueNotifier,
builder: (context, List<Food> value, widget) {
var list = value;
return ListView.builder(
itemCount: list.length,
itemBuilder: (context, index) {
return Text(list[index].name);
}
);
},
);
}
}
You get this as output:
Also checkout this Gist you can run on DartPad.dev to check out how it works.

Flutter : setState() called in constructor: _SharesListState#6c96a(lifecycle state: created, no widget, not mounted)

Error message: This happens when you call setState() on a State object for a widget that hasn't been inserted into the widget tree yet. It is not necessary to call setState() in the constructor, since the state is already assumed to be dirty when it is initially created.
Really don't understand what it means =/.
class SharesHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Header(),
Expanded(
child: SharesList(),
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_SharesListState shareListState = _SharesListState();
final ShareModel shareModel = ShareModel('Embraer', 'EMBR3', 13.50, 15.20);
shareListState.addShare(shareModel);
}),
);
}
}
class SharesList extends StatefulWidget {
final List<ShareModel> sharesList = [];
#override
_SharesListState createState() => _SharesListState();
}
class _SharesListState extends State<SharesList> {
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: widget.sharesList.length,
itemBuilder: (context, index) => ShareItem(widget.sharesList[index]),
);
}
void addShare(ShareModel shareModel) {
if (shareModel != null) {
setState(() {
widget.sharesList.add(shareModel);
});
}
}
}
** I've tried to put "if(this.mounted) to avoid the error, but why isn't the widget mounted?
Try with using this: shareModel != null && ismounted
It checks if the widget is mounted in the tree, and if that's true, it'll setState.

I want to check if any Widget exist in a List?

I want to load pages from a List and when the user taps on an item from the drawer he can go to that page (if it's already opened) otherwise the Widget will load in the selected page.
But I can't find if that widget is already exists in the List if(myList.contains(Widget1())) => print('it exist'); One guy told me to override hashCode and operator==
class Widget6 extends StatelessWidget {
final String title = 'Widget6';
final Icon icon = Icon(Icons.assessment);
#override
Widget build(BuildContext context) {
return Center(
child: icon,
);
}
#override
bool operator ==(dynamic other) {
final Widget6 typedOther = other;
return title == typedOther.title && icon == typedOther.icon;
}
#override
int get hashCode => hashValues(title, icon);
}
if I do that I can't use any child widget to those widgets. Getting exception like: type 'Center' is not a subtype of type 'Widget6'. I copied this from flutter gallery I didn't find good documentation/guide. Sorry, I am a beginner.
Complete code below
class _MyHomePageState extends State<MyHomePage> {
List pageList = [
Widget1(),
Widget2(),
Widget3(),
Widget4(),
];
PageController _pageController;
int _selectedIndex = 0;
#override
void initState() {
_pageController = PageController(
initialPage: _selectedIndex,
);
super.initState();
}
void navigatePage(Widget widget) {
// problem is here
if (pageList.contains(widget)) {
_pageController.animateToPage(pageList.indexOf(widget, 0),
duration: Duration(milliseconds: 300), curve: Curves.ease);
}
else {
setState(() {
pageList.removeAt(_pageController.page.toInt());
pageList.insert(_pageController.page.toInt(), widget);
});
_pageController.animateToPage(_pageController.page.toInt(),
duration: Duration(milliseconds: 300), curve: Curves.ease);
}
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text('Widget1'),
onTap: () => navigatePage(
Widget1(),
),
),
ListTile(
title: Text('Widget2'),
onTap: () => navigatePage(
Widget2(),
),
),
ListTile(
title: Text('Widget3'),
onTap: () => navigatePage(
Widget3(),
),
),
ListTile(
title: Text('Widget4'),
onTap: () => navigatePage(
Widget4(),
),
),
ListTile(
title: Text('Widget5'),
onTap: () => navigatePage(
Widget5(),
),
),
ListTile(
title: Text('Widget6'),
onTap: () => navigatePage(
Widget6(),
),
),
],
),
),
appBar: AppBar(
title: Text(widget.title),
),
body: PageView.builder(
onPageChanged: (newPage) {
setState(() {
this._selectedIndex = newPage;
});
},
controller: _pageController,
itemBuilder: (context, index) {
return Container(
child: pageList[index],
);
},
itemCount: pageList.length,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (index) => setState(() {
_selectedIndex = index;
_pageController.animateToPage(index,
duration: Duration(milliseconds: 300), curve: Curves.ease);
}),
items: pageList.map((page) {
return BottomNavigationBarItem(
backgroundColor: Colors.deepOrangeAccent,
icon: page.icon,
title: Text(page.title));
}).toList(),
),
);
}
}
Here List of dummy Widgets
class Widget1 extends StatelessWidget {
final String title = 'Widget1';
final Icon icon = Icon(Icons.school);
#override
Widget build(BuildContext context) {
return Center(
child: icon,
);
}
}
class Widget2 extends StatelessWidget {
// only title and icon are changed
}
class Widget3 extends StatelessWidget {
// only title and icon are changed
}
class Widget4 extends StatelessWidget {
// only title and icon are changed
}
class Widget5 extends StatelessWidget {
// only title and icon are changed
}
class Widget6 extends StatelessWidget {
// only title and icon are changed
}
Okay, I found the solution. And it has to do with operator== overriding
I missed this line if (runtimeType != other.runtimeType) return false;
The whole code stays the same.
#override
// ignore: hash_and_equals
bool operator ==(dynamic other) {
if (runtimeType != other.runtimeType) return false;
final Widget6 typedOther = other;
return title == typedOther.title;
}
#Ahmed Sorry for the late reply, I decided to put it in an answer rather than a comment.
One solution is yours, overriding == but I was thinking of using Key and then instead of using contains method, using something like:
if(myList.indexWhere((Widget widget)=> widget.key==_key) != -1)...
Suggestion
You can store icon and title as a map or a module instead of making 6 different Widget.
You can create another file, saying module.dart like this:
class Module {
final String title;
final Icon icon;
Module(this.title, this.icon);
#override
int get hashCode => hashValues(title.hashCode, icon.hashCode);
#override
bool operator ==(other) {
if (!identical(this, other)) {
return false;
}
return other is Module &&
this.title.compareTo(other.title) == 0 &&
this.icon == other.icon;
}
}
Then create another file that builds the page, saying mywidget.dart, like this:
class MyWidget extends StatelessWidget {
final Module module;
MyWidget({Key key,#required this.module}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: module.icon,
);
}
}
Then on each ListTile's onTap, Navigate like this:
...
ListTile(
title: Text('Widget1'),
onTap: () => navigatePage(
MyWidget(module: Module('Widget1', Icon(Icons.school)),)
),
),
...
So instead of storing Widgets, you store a Type(Here Module) that you declared.
You can also use the list's map to build each ListTile of the ListView for each Module, instead of doing it one by one. (if each item on the drawer are similar), Something like this:
List<Module> myTabs = [
Module('Widget1', Icon(Icons.school)),
Module('Widget2', Icon(Icons.home)),
];
...
Drawer(
child: ListView(
children:myTabs.map((Module module)=> ListTile(
title:Text( module.title),
onTap: navigatePage(MyWidget(module: module,)),
)).toList(),
) ,
);
...

How to implement a Slider within an AlertDialog in Flutter?

I am learning app development on Flutter and can't get my Slider to work within the AlertDialog. It won't change it's value.
I did search the problem and came across this post on StackOverFlow:
Flutter - Why slider doesn't update in AlertDialog?
I read it and have kind of understood it. The accepted answer says that:
The problem is, dialogs are not built inside build method. They are on a different widget tree. So when the dialog creator updates, the dialog won't.
However I am not able to understand how exactly does it have to be implemented as not enough background code is provided.
This is what my current implementation looks like:
double _fontSize = 1.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(qt.title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.format_size),
onPressed: () {
getFontSize(context);
},
),
],
),
body: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 15.0),
itemCount: 3,
itemBuilder: (context, index) {
if (index == 0) {
return _getListTile(qt.scripture, qt.reading);
} else if (index == 1) {
return _getListTile('Reflection:', qt.reflection);
} else {
return _getListTile('Prayer:', qt.prayer);
}
})
);
}
void getFontSize(BuildContext context) {
showDialog(context: context,builder: (context){
return AlertDialog(
title: Text("Font Size"),
content: Slider(
value: _fontSize,
min: 0,
max: 100,
divisions: 5,
onChanged: (value){
setState(() {
_fontSize = value;
});
},
),
actions: <Widget>[
RaisedButton(
child: Text("Done"),
onPressed: (){},
)
],
);
});
}
Widget parseLargeText(String text) {...}
Widget _getListTile(String title, String subtitle) {...}
I understand that I will need to make use of async and await and Future. But I am not able to understand how exactly. I've spent more than an hour on this problem and can't any more. Please forgive me if this question is stupid and noobish. But trust me, I tried my best.
Here is a minimal runnable example. Key points:
The dialog is a stateful widget that stores the current value in its State. This is important because dialogs are technically separate "pages" on your app, inserted higher up in the hierarchy
Navigator.pop(...) to close the dialog and return the result
Usage of async/await
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double _fontSize = 20.0;
void _showFontSizePickerDialog() async {
// <-- note the async keyword here
// this will contain the result from Navigator.pop(context, result)
final selectedFontSize = await showDialog<double>(
context: context,
builder: (context) => FontSizePickerDialog(initialFontSize: _fontSize),
);
// execution of this code continues when the dialog was closed (popped)
// note that the result can also be null, so check it
// (back button or pressed outside of the dialog)
if (selectedFontSize != null) {
setState(() {
_fontSize = selectedFontSize;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Font Size: ${_fontSize}'),
RaisedButton(
onPressed: _showFontSizePickerDialog,
child: Text('Select Font Size'),
)
],
),
),
);
}
}
// move the dialog into it's own stateful widget.
// It's completely independent from your page
// this is good practice
class FontSizePickerDialog extends StatefulWidget {
/// initial selection for the slider
final double initialFontSize;
const FontSizePickerDialog({Key key, this.initialFontSize}) : super(key: key);
#override
_FontSizePickerDialogState createState() => _FontSizePickerDialogState();
}
class _FontSizePickerDialogState extends State<FontSizePickerDialog> {
/// current selection of the slider
double _fontSize;
#override
void initState() {
super.initState();
_fontSize = widget.initialFontSize;
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Font Size'),
content: Container(
child: Slider(
value: _fontSize,
min: 10,
max: 100,
divisions: 9,
onChanged: (value) {
setState(() {
_fontSize = value;
});
},
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
// Use the second argument of Navigator.pop(...) to pass
// back a result to the page that opened the dialog
Navigator.pop(context, _fontSize);
},
child: Text('DONE'),
)
],
);
}
}
You just need to warp the AlertDialog() with a StatefulBuilder()