Flutter, set state() from parent widget called by not rendering - flutter

I have three screen.
Home screen. 2 Mortgage Screen. 3. New branch Screen. [Each Mortgage can have one or more branches]
The home screen shows a list of all current mortgages a user ended, with a summary of each the branches in each mortgages.
When the user clicks on one of the mortgages in the list in screen 1, he gets to screen 2 which shows all the details of the branches of that mortgage. User can add new branch by clicking floating action button, to get to page 3.
In page 3, the user fills out a form to add a new branch. Once a branch is added, page 3 is popped, and page 2 is still appearing.
When page 3 is done, a new branch is added to the selected mortgage, and it is supposed to update the data displayed in page 2 and in page 1. I have done this by passing callback methods into pages 2 and 1, and then calling set state in both classes.
Page 2 is updated and displays fine. However, when I go back from page 2 to page 1, page 1 has not updated. Even though the setState method is called in page 1.
I hope its clear, I will add the code of page 1, and maybe you can help me see why the page is not rerendering.
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List<MaslulModel> savedMaslulim = <MaslulModel>[];
List<MortgageModel> savedMortgages = <MortgageModel>[];
// THIS METHOD IS CALLED FROM PAGE 2.
notifyHomeScreen() async {
print('2124: notifyHomeScreen called in home_screen');
savedMaslulim.clear();
savedMortgages.clear();
savedMaslulim = await SharedPrefsMethods.getMaslulListFromPrefs();
for (var i = 0; i < savedMaslulim.length; i++) {
print(savedMaslulim[i].getDetails());
}
savedMortgages = sortOutMaslulimToMortgages(savedMaslulim);
setState(() {
print('2124: Set state. Maslul at 0 List size: ${savedMortgages[0].maslulList.length}');
});
}
TextEditingController _textFieldController = TextEditingController();
String codeDialog = '';
String valueText = '';
#override
initState() {
super.initState();
print('InitState');
asyncGetSavedMortgages();
}
void asyncGetSavedMortgages() async {
savedMaslulim = await SharedPrefsMethods.getMaslulListFromPrefs();
savedMortgages = sortOutMaslulimToMortgages(savedMaslulim);
print(savedMortgages.length);
setState(() {
print('Set state called');
});
}
#override
Widget build(BuildContext context) {
for (var i = 0; i < savedMortgages.length; i++) {
if(savedMortgages[i].name=='tonight'){
print('2124: From HOME: ${savedMortgages[i].maslulList.length}');
}
}
return Scaffold(
appBar: AppBar(title: Text(AppLocalizations.of(context)!.translate('my_mortgages'))),
drawer: MainDrawer(),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
// Navigator.pushNamed(context, '/new_mortgage_screen');
_displayTextInputDialog(context);
},
label: Text('הוסף משכנתא'),
icon: Icon(Icons.add),
backgroundColor: Colors.pink,
),
body: ListView.builder(
itemCount: savedMortgages.length,
key: Key(savedMortgages.length.toString()),
itemBuilder: (context, index){
for (var i = 0; i < savedMortgages.length; i++) {
if(savedMortgages[i].name=='tonight'){
print('2124: From HOME itemBuilder: ${savedMortgages[i].maslulList.length}');
}
}
return MortgageSummaryWidget(savedMortgages[index], notifyHomeScreen: notifyHomeScreen );
},
),
);
}
Future<void> _displayTextInputDialog(BuildContext context) async {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('הכנס שם של המשנכתא:'),
content: TextField(
onChanged: (value) {
setState(() {
valueText = value;
});
},
controller: _textFieldController,
decoration: InputDecoration(hintText: "שם"),
),
actions: <Widget>[
FlatButton(
color: Colors.white,
textColor: Colors.red,
child: Text('בטל'),
onPressed: () {
setState(() {
Navigator.pop(context);
});
},
),
FlatButton(
color: Colors.blue,
textColor: Colors.white,
child: Text('בצע'),
onPressed: () {
setState(() {
codeDialog = valueText;
if(codeDialog.isEmpty){
showAlertDialog(context, 'שגיאה', 'לא הכנסת שם מסלול');
return;
}
Navigator.pop(context);
// Navigator.pushNamed(context, '/new_mortgage_screen');
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => NewMortgageScreen(notifyParent: notifyHomeScreen, title: codeDialog,)));
// Navigator.pushNamed(
// context,
// '/new_mortgage_screen',
// arguments: {'mortgageName': codeDialog}
// );
});
},
),
],
);
});
}
}
All the values are updated, but the screen display isn't.
I cannot figure this out. Thanks

I realised the problem, I was sending a parameter into the State, and this wan't getting updated. I changed it to get the parameter by using widget.parameter.

Related

Flutter: My notifyListeners() doesn't work, but only in the release apk

I have a page that shows a loading while making my API call, and once the call is done it shows the received data.
On debugger everything works correctly, but when I create the apk with 'flutter build apk', and download it, the loading remains indefinitely.
I also put a showDialog at the end of my Provider function that makes the API call (I put this showDialog just below notifyListeners().
I can't understand why in debug it works and in release it doesn't.
(This notifyListeners thing not working just does it for every API call I make)
This is the code of the provider function that makes the api call:
Future<void> getUserSites(context) async {
_userSites.clear();
isLoading = true;
notifyListeners();
try {
final response = await NetworkService.call(
url: '/api/structure/Sites',
method: Method.Get,
context: context) as List<dynamic>;
for (var i = 0; i < response.length; i++) {
_userSites.add(Sites.fromJson(response.elementAt(i)));
}
if (defaultSite == null) {
if (SimplePreferences.getDefaultSite() == null) {
defaultSite = _userSites.isNotEmpty ? _userSites.first : null;
if (defaultSite != null) {
SimplePreferences.setDefaultSite(defaultSite!.id);
}
} else {
defaultSite = _userSites.firstWhere(
(element) => element.id == SimplePreferences.getDefaultSite()!);
}
}
} catch (e) {
inspect(e);
if (SimplePreferences.getToken() != null) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('General Error'),
content: Text(e.toString()),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
'Ok',
),
)
],
),
);
}
// throw e;
}
isLoading = false;
notifyListeners();
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('getUserSites done!'),
content: Text(_userSites.toString()),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
'Ok',
),
)
],
),
);
}
this is the Home page code:
class HomePageScreen extends StatelessWidget { const HomePageScreen({super.key}); static const String routeName = '/';
#override Widget build(BuildContext context) { log('New Page: Home Page'); final provider = Provider.of<MyManager>(context);
return provider.isLoading ? const Center(
child: CircularProgressIndicator(),
)
: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MainButton(
onTap: () async {
Navigator.of(context)
.pushNamed(ShowPatrolScreen.routeName);
await provider.getPatrol(context);
},
icon: Icons.home,
title: 'ShowPatrol',
),
printSito(provider.defaultSite?.description ?? 'Nessun Sito', context),
PrintRequestZ(
showCompleted: false,
),
],
),
),
);
}
Widget printSito(String name, context) { .... //pass context for Navigator and Theme } } `
this is the main page:
...
final myScreens = [
const HomePageScreen(),
...
];
#override
void initState() {
// TODO: implement initState
super.initState();
print('token: ${SimplePreferences.getToken()}');
if (SimplePreferences.getToken() == null){
Navigator.of(context).pushReplacementNamed('/Auth');
}
var provider = Provider.of<MyManager>(context, listen: false);
provider.setAll(context); //this function calls all my API calls, but for testing, I commented out all other functions and kept only the one written above
}
#override
Widget build(BuildContext context) {
var provider = Provider.of<MyManager>(context);
return Scaffold(
appBar: const MyAppBar(title: 'Ronda',canGoBack: false,),
body: myScreens[currentPage],
bottomNavigationBar: ...
),
}
Thanks in advance!
after some research i found the solution.
You have to use WidgetsBinding.instance.addPostFrameCallback
in the parent component.
So my home page now looks like this:
#override
void initState() {
// TODO: implement initState
super.initState();
print('token: ${SimplePreferences.getToken()}');
if (SimplePreferences.getToken() == null){
Navigator.of(context).pushReplacementNamed('/Auth');
}
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
var provider = Provider.of<MyManager>(context, listen: false);
provider.setAll(context); //this function calls all my API calls, but for testing, I commented out all other functions and kept only the one written above
});
}
I don't quite understand why though. If someone could explain it to me, I'd be very happy
Use Consumer to access the Provider's Variable
return Consumer<YourProviderName>(builder : (context, value, child){
return value.isLoading? const Center(
child: CircularProgressIndicator(),
):YourWidget(),
});

Riverpood does not update the status until I enter the screen

I have a general configuration screen, with a button that syncs the data
(...)
appBar: AppBar(
actions: [
Row(
children: [
const Text(ConfigurationsStringsUI.updateGeneral),
IconButton(
icon: const Icon(Icons.sync),
onPressed: () {
ref.read(listProductController.notifier).syncProducts();
ref.read(listEmployedController.notifier).syncEmployees();
},
),
],
)
],
),
(...)
In the case of products, it has a specific screen that is responsible for managing them, basically a CRUD. When I press the sync button, the idea is to connect to supabase and update the data. While this is happening display a loadign. The problem is that the loading does not appear.
products_page.dart
GetIt sl = GetIt.instance;
class CRUDProduct extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
actions: [
IconButton(
onPressed: () {
ref.read(listProductController.notifier).syncProducts();
},
icon: const Icon(Icons.update),
)
],
),
floatingActionButton: ref.watch(isVisibleFabProducts)
? FloatingActionButton(
onPressed: () {
showDialog(
context: scaffoldKey.currentContext!,
builder: (context) => AddProductDialog(),
);
},
child: const Icon(Icons.fastfood),
)
: null,
body: ref.watch(listProductController).when(
data: (products) {
if (products.isEmpty) {
return const Center(
child: Text(ProductStringsUI.emptyList),
);
} else {
return NotificationListener<UserScrollNotification>(
onNotification: (notification) {
if (notification.direction == ScrollDirection.forward) {
ref.read(isVisibleFabProducts.notifier).state = true;
}
if (notification.direction == ScrollDirection.reverse) {
ref.read(isVisibleFabProducts.notifier).state = false;
}
return true;
},
child: ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) {
return ItemProductList(product: products[index]);
},
separatorBuilder: (_, __) => const Divider(
color: Colors.grey,
),
itemCount: products.length),
);
}
},
error: (error, stackTrace) {
return const Center(
child: Text(ProductStringsUI.errorList),
);
},
loading: () {
return const Center(child: CircularProgressIndicator());
},
));
}
}
Product provider:
final listProductController =
StateNotifierProvider<ProductController, AsyncValue<List<LocalProduct>>>(
(ref) => ProductController(ref));
product_controller.dart
class ProductController extends StateNotifier<AsyncValue<List<LocalProduct>>> {
ProductController(this._ref) : super(const AsyncValue.loading()) {
getProducts();
}
final Ref _ref;
Future<void> getProducts() async {
try {
final employees = await sl.get<ListProductUseCase>().getProducts();
if (mounted) {
state = AsyncValue.data(employees);
}
} catch (e) {
state = AsyncValue.error(e, StackTrace.current);
}
}
Future<void> syncProducts() async {
try {
_ref.read(listCategoryController.notifier).state =
const AsyncValue.loading();
_ref.read(listEmployedController.notifier).state =
const AsyncValue.loading();
state = const AsyncValue.loading();
await _ref.read(listCategoryController.notifier).syncCategory();
final employees = await sl.get<SyncProductUseCase>().syncProducts();
state.whenData((value) {
if (mounted) {
state = AsyncValue.data([...value, ...employees]);
}
});
_ref.invalidate(listProductController);
} catch (e) {
state = AsyncValue.error(e, StackTrace.current);
}
}
}
In the case of products, it has a specific screen that is responsible for managing them, basically a CRUD. When I press the sync button, the idea is to connect to supabase and update the data. While this is happening display a loadign. The problem is that the loading does not appear. There are two scenarios:
1-I open the app, I press the sync button on the configuration screen, I enter the screen in charge of managing the products, I see the loaded products, and at the moment it updates me with the new data, when I should see the loading and then the new ones data.
In this scenario is where my biggest doubt about the strange behavior is.
2-I open the app, I enter the screen in charge of managing the products, I go to the configuration screen, I press sync, and in that case if I go to enter if the loading appears
The same goes for employees.
When you have an async provider in Riverpod, you should tell it to load:
Future<void> addTopic(String name) async {
state = const AsyncValue.loading(); // Here
state = await AsyncValue.guard(() async { // And then you guard the value
// Inside the brackets do all the logic of the function
final currentId = ref.watch(currentSubjectProvider);
final topicRepo = ref.watch(topicRepoProvider);
await topicRepo.addTopic(currentId!, name);
return _getTopics();
});
}
This example of mine, is a real project I am working on, and this is loading as expected, but I should mention that I am using the Riverpod generator, so if the generator did something, I am unaware of it.
If you set the state to loading and guard the value, all listerners of that provider should be loading correctly.

Flutter Won't Load New List Builder page

I have been working on this for several hours and I give up. I need help please. I am selecting a BottomNavBarItem onTap and I launch a void function called _onItemTapped which then determines the index of the BNBI then launches a new Class. I have a couple test prints in the code so I can see that it gets down to the second 'print 'testing1'' but nothing loads and nothing happens. Can someone please explain what I am doing wrong?
Code below from the BNBI code
BottomNavigationBarItem(
backgroundColor: Colors.blueGrey,
icon: Icon(Icons.login),
label: ' Search \n Ingredients Db',
tooltip:
'Login or Create an Account to Search Ingredients Database',
),
],
onTap: _onItemTapped,
//loads
void _onItemTapped(final int index) {
setState(() {
Future.sync(() => _CocPageState());
_selectedIndex = index;
});
if (_selectedIndex == 0) {
_CocPageState()
.build(context); // takes you to the Chemicals of Concern page.
} else if (_selectedIndex == 1) {
_CocPageState().build(context); // //todo go to the dirty list
} else if (_selectedIndex == 2) {
_CocPageState().build(context); //todo go to the safer products list
}
_CocPageState().build(context);
}
// to the Class
class CocPage extends StatefulWidget {
#override
_CocPageState createState() => _CocPageState();
}
class _CocPageState extends State<CocPage> {
#override
Widget build(BuildContext context) {
print('test');
const title =
'Chemicals of Concern';
print('testing1'); //nothing happens beyond this point that I can tell.
return Scaffold(
body: ListView.builder(
itemCount: chemBrain.chemsBank.length,
// itemBuilder: (context, index),
prototypeItem: ListTile(
title: Text('Index 0: chemBrain.chemsBank[index].chemName')),
itemBuilder: (BuildContext context, index) {
return ListTile(
title: Text(chemBrain.chemsBank[index].chemName),
trailing: Column(
children: [
InkWell(
child: Text(chemBrain.getChemsName()),
I would suggest that you call Navigator.of(context).push() when you tap on the BottomNavigationBarItem.Then you simply push to a new screen and don't have to worry about state.

How to change the color of one button among several buttons in Flutter?

I am new to Dart and the Flutter framework. Currently, I have a GridView populated with 25 buttons. Each button, by default, has an orange background color. However, I want to give an option to the user to long press on any button and a PopUpMenu shows up, giving them the option to pick between choosing a different color for the button. Here are the two things I have tried:
Set a global variable that changes the color. However, when I change its state, it changes the color of ALL the buttons (I only want the color of the button selected to get changed).
Pass a local variable through the instantiation of the button, and pass that variable along to the PopUpMenu. However, this does not change anything about the buttons.
How do I go about solving this problem? I am including snippets of code below to help you out. Note that this code refers to how #2 was implemented.
The 25-Button Instantiation:
// Random number generator
var _randGen = new Random();
//List of maze cards
List<Widget> mazeCards = new List();
// Generate cards until it has 25 cards within the list
while(mazeCards.length != 25)
{
// Get the index
var _currIndex = _randGen.nextInt(words.length);
// Add the card to the list
var _cardColor = Colors.orange;
mazeCards.add(createCard(words[_currIndex], _cardColor));
}
The createCard Method:
Widget createCard(String someString, Color _cardColor)
{
return GestureDetector(
onTapDown: _storePosition,
child: Container(
padding: EdgeInsets.all(8.0),
child:
_createButton(someString, _cardColor)
),
);
}
The createButton Method:
Widget _createButton(String someString, Color _cardColor)
{
Widget newButton = MaterialButton(
padding: EdgeInsets.all(50.0),
color: _cardColor,
onPressed: () => _printButtonText(someString),
onLongPress: () {
cardOptionsMenu(_cardColor);
},
textTheme: ButtonTextTheme.primary,
//_someColor(),
child: Text(someString)
);
return newButton;
}
The cardOptionsMenu Method:
void cardOptionsMenu(Color _cardColor)
{
final RenderBox overlay = Overlay.of(context).context.findRenderObject();
showMenu(
context: context,
...
)
.then<void>((CardOptionEnum cardOption) {
if (cardOption == null) return;
else{
switch (cardOption)
{
case CardOptionEnum.makeBlackCard:
setState(() {
_cardColor = Colors.black;
});
break;
case CardOptionEnum.makeBlueCard:
setState(() {
_cardColor = Colors.blue;
});
break;
case CardOptionEnum.makeRedCard:
setState(() {
_cardColor = Colors.red;
});
break;
case CardOptionEnum.makeYellowCard:
setState(() {
_cardColor = Colors.yellow;
});
break;
case CardOptionEnum.changeWord:
break;
}
}
});
}
List<int> items = [];
List<Color> colors = [];
#override
void initState() {
super.initState();
items = List.generate(25, (ind) => ind).toList();
colors = List.generate(25, (ind) => Colors.orange).toList();
}
#override
Widget build(BuildContext context) {
return GridView.builder(
itemCount: items.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (con, ind) {
return InkWell(
child: Card(child: Text('${items[ind]}',
style:TextStyle(color:Colors.white),
textAlign:TextAlign.center), color: colors[ind]),
onTap: () {
changeColor(ind);
});
});
}
void changeColor(index) {
showDialog(
context: context,
builder: (con) {
return AlertDialog(
title: Text('Choose a color !'),
content: Column(mainAxisSize: MainAxisSize.min, children: [
ListTile(
title: Text('Blue'),
onTap: () {
Navigator.of(con).pop();
changeState(index, Colors.blue);
}),
ListTile(
title: Text('Red'),
onTap: () {
Navigator.of(con).pop();
changeState(index, Colors.red);
}),
ListTile(
title: Text('Green'),
onTap: () {
Navigator.of(con).pop();
changeState(index, Colors.green);
})
]),
);
});
}
void changeState(index, color) {
setState(() {
colors[index] = color;
});
}

Dynamic list of check box tile in alert dialog not working

There is no clear answer on how to implement a checkbox tile in a dialog and set the state to work.
A print statement is working in setting the state of the checkbox is not changing, but other statements are working. Where can I find the answer?
I am using a dialog with multiple check boxes for multi select. Is there another of implementing multiselect in Flutter?
child: TextFormField(
decoration: InputDecoration(
labelText: 'Team Leader',
labelStyle: TextStyle(color: Colors.black)),
controller: teamLeaderController,
enabled: false,
style: TextStyle(color: Colors.black),
),
onTap: () {
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return CheckBoxDialog(context, teamLeader,
"Choose Team Leader", teamLeaderController, onSubmit);
});
}),
class CheckBoxState extends State<CheckBoxDialog> {
BuildContext context;
List<String> places;
String title;
TextEditingController con;
bool state;
CheckBoxState(this.context, this.places, this.title, this.con);
#override
void initState() {
super.initState();
state = false;
}
#override
Widget build(BuildContext context) {
return new AlertDialog(
title: new Text(title),
content:
Column(children: getMultiSelectOption(context, places, con, state)),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
child: Text('Ok'),
onPressed: () {
widget.onSubmit("");
Navigator.of(context).pop();
})
],
);
}
List<Widget> getMultiSelectOption(BuildContext context, List<String> places,
TextEditingController con, bool state) {
List<Widget> options = [];
List<String> selectedList = [];
for (int i = 0; i < places.length; i++) {
options.add(CheckboxListTile(
title: Text(places[i]),
value: selectedList.contains(places[i]),
onChanged: (bool value) {
print("on change: $value title: ${places[i]}");
setState(() {
if (value) {
selectedList.add(places[i]);
} else {
selectedList.remove(places[i]);
}
print("contains: ${selectedList.contains(places[i])}");
print("status: $value");
});
}));
}
return options;
}
}
Suppose you have a Dialog with some Widgets such as RadioListTile, DropdowButton… or anything that might need to be updated WHILE the dialog remains visible, how to do it?
Look at this example here.
https://www.didierboelens.com/2018/05/hint-5-how-to-refresh-the-content-of-a-dialog-via-setstate/
Suppose you have a Dialog with some Widgets such as RadioListTile, DropdowButton… or anything that might need to be updated WHILE the dialog remains visible, how to do it?
Difficulty: Beginner
Background
Lately I had to display a Dialog to let the user select an item from a list and I wanted to display a list of RadioListTile.
I had no problem to show the Dialog and display the list, via the following source code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class Sample extends StatefulWidget {
#override
_SampleState createState() => new _SampleState();
}
class _SampleState extends State<Sample> {
List<String> countries = <String>['Belgium','France','Italy','Germany','Spain','Portugal'];
int _selectedCountryIndex = 0;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){_showDialog();});
}
_buildList(){
if (countries.length == 0){
return new Container();
}
return new Column(
children: new List<RadioListTile<int>>.generate(
countries.length,
(int index){
return new RadioListTile<int>(
value: index,
groupValue: _selectedCountryIndex,
title: new Text(countries[index]),
onChanged: (int value) {
setState((){
_selectedCountryIndex = value;
});
},
);
}
)
);
}
_showDialog() async{
await showDialog<String>(
context: context,
builder: (BuildContext context){
return new CupertinoAlertDialog(
title: new Text('Please select'),
actions: <Widget>[
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Cancel');},
child: new Text('Cancel'),
),
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Accept');},
child: new Text('Accept'),
),
],
content: new SingleChildScrollView(
child: new Material(
child: _buildList(),
),
),
);
},
barrierDismissible: false,
);
}
#override
Widget build(BuildContext context) {
return new Container();
}
}
I was surprised to see that despite the setState in lines #34-36, the selected RadioListTile was not refreshed when the user tapped one of the items.
Explanation
After some investigation, I realized that the setState() refers to the stateful widget in which the setState is invoked. In this example, any call to the setState() rebuilds the view of the Sample Widget, and not the one of the content of the dialog. Therefore, how to do?
Solution
A very simple solution is to create another stateful widget that renders the content of the dialog. Then, any invocation of the setState will rebuild the content of the dialog.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class Sample extends StatefulWidget {
#override
_SampleState createState() => new _SampleState();
}
class _SampleState extends State<Sample> {
List<String> countries = <String>['Belgium','France','Italy','Germany','Spain','Portugal'];
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){_showDialog();});
}
_showDialog() async{
await showDialog<String>(
context: context,
builder: (BuildContext context){
return new CupertinoAlertDialog(
title: new Text('Please select'),
actions: <Widget>[
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Cancel');},
child: new Text('Cancel'),
),
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Accept');},
child: new Text('Accept'),
),
],
content: new SingleChildScrollView(
child: new Material(
child: new MyDialogContent(countries: countries),
),
),
);
},
barrierDismissible: false,
);
}
#override
Widget build(BuildContext context) {
return new Container();
}
}
class MyDialogContent extends StatefulWidget {
MyDialogContent({
Key key,
this.countries,
}): super(key: key);
final List<String> countries;
#override
_MyDialogContentState createState() => new _MyDialogContentState();
}
class _MyDialogContentState extends State<MyDialogContent> {
int _selectedIndex = 0;
#override
void initState(){
super.initState();
}
_getContent(){
if (widget.countries.length == 0){
return new Container();
}
return new Column(
children: new List<RadioListTile<int>>.generate(
widget.countries.length,
(int index){
return new RadioListTile<int>(
value: index,
groupValue: _selectedIndex,
title: new Text(widget.countries[index]),
onChanged: (int value) {
setState((){
_selectedIndex = value;
});
},
);
}
)
);
}
#override
Widget build(BuildContext context) {
return _getContent();
}
}