Flutter: RefreshIndicator reload - flutter

I have a listitem on RefreshIndicator and each of the items have a navigator.pushNamed which redirect a new page.
I need reload RefreshIndicator when i run Navigator.pop(context) in the new page.
//Home
RefreshIndicator(
onRefresh: _con.refreshHome,
child: ListView.separated(
itemBuilder: (context, index) => SizedBox(
width: size.width * 0.8,
child: OrdenWidget(
orden: currentData[index],
mostrarComenzar: true,
onTap: currentData[index].aceptada ==
true
? () {
Navigator.PushNamed(
context,
"/New Page",
arguments: currentData[index],
).then((_) {
_con.refreshHome();
});
}
: null,
),
),
separatorBuilder: (context, index) => const Divider(
color: Colors.white,
),
itemCount: currentData.length,
),
),
//Second Screen
TextButton(onPressed:(){ Navigator.pop(context);}, child: const Text("Go Back"),),

try with Navigator.push
Navigator.push(
context,
MaterialPageRoute<void>(builder: (_) => NewPage()),
).then((_){
_con.refreshHome();
});

Related

Flutter - Radio List Tile not selecting the radio button inside bottom sheet

I am using flutter RadioListTile for radio button selection and having issue while trying to checked. i.e, not selecting the button but value is receiving after checked.
can any one help on this?
Below is the sample code,
var _selectedLanguageRadioIndex = 0;
List<String> languageTitle = ["English","हिंदी","ಕನ್ನಡ"];
List<String> languageType = ["en","hi","ka"];
_languageOptions(){
showModalBottomSheet(
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
),
context: context,
builder: (context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter myState) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.fromLTRB(15, 10, 0, 10),
Flexible(
fit: FlexFit.loose,
child: ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) {
return Container(
child: RadioListTile(
value: index,
groupValue: _selectedLanguageRadioIndex,
selected: _selectedLanguageRadioIndex == index,
onChanged: (val) => {
setState(() => _selectedLanguageRadioIndex = val as int),
},
title: ListTile(
title: Text(languageTitle[index]
),
controlAffinity: ListTileControlAffinity.trailing,
),
);
},
itemCount: languageTitle.length,
separatorBuilder: (context, index) => Divider(
thickness: 1,
),
)
)
],
);
},
);
}
);
}
Since you are using StatefulBuilder Widget, it creates its own State, in your code its StateSetter myState. It means the BottomSheet Widget state is controlled and changed by myState keyword. In your BottomSheet Widget you are calling setState which tells dart to update the UI with respect to the Widgets linked by setState and hence it does not update the UI.
In your code just replace the following line : -
setState(() =>
_selectedLanguageRadioIndex = val as int), //<-- Replace this line
myState(() =>
_selectedLanguageRadioIndex = val as int), //<-- Replace it with this line
Complete Code : -
var _selectedLanguageRadioIndex = 0;
List<String> languageTitle = ["English", "हिंदी", "ಕನ್ನಡ"];
List<String> languageType = ["en", "hi", "ka"];
_languageOptions() {
showModalBottomSheet(
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
),
context: context,
builder: (context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter myState) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.fromLTRB(15, 10, 0, 10),
child: ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) {
return RadioListTile(
value: index,
groupValue: _selectedLanguageRadioIndex,
selected: _selectedLanguageRadioIndex == index,
onChanged: (val) => {
myState(() =>
_selectedLanguageRadioIndex = val as int),
},
title: ListTile(
title: Text(languageTitle[index]),
),
);
},
itemCount: languageTitle.length,
separatorBuilder: (context, index) => const Divider(
thickness: 1,
),
))
],
);
},
);
});
}

Issue with statefulWidget unable to make desired changes

I am working on a statefulWidget and my purpose is to make sure that the next button is not clickable until an option (in this language is selected). However it doesn't seem to work, I also added Yaesin's(Someone who answered) answer to the code
ListView.builder(
itemCount: histoires.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
histoires[index].title,
style: TextStyle(color: Colors.pink),
),
trailing: IconButton(
icon: Icon(Icons.play_arrow),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) =>
AlertDialog(
content: Column(children: [
InkWell(
onTap: () {
_handleTap;
},
child: ListTile(
trailing: Icon(Icons
.flag_circle_rounded),
title: Text(
"French",
style: TextStyle(
color: Colors
.blueGrey),
))),
_active
? InkWell(
onTap: () {},
child: Image.asset(
"assets/nextactive.png",
height: height * 0.2,
width: width * 0.4),
)
: Image.asset(
"assets/nextinactive.png",
height: height * 0,
width: width * 0)
]),
));
});
}));
}),
To update dialog UI, you can use StatefulBuilder's setState
return StatefulBuilder(
builder: (context, setState) =>
AlertDialog(
content: Column(children: [
While using separate method, pass the StatefulBuilder's setState to the function. For your case, it will be
onPressed: () async {
await showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setStateSB) => AlertDialog(
content: Column(children: [
InkWell(
onTap: () {
_handleTap(setStateSB);
},
child: ListTile(
Also make sure to receive this setStateSB(renamed to avoid confusion with state's setState).
_handleTap(setStateSB){ ....
More about using StatefulBuilder
Since your in a Dialog, for setState to work, you need to wrap it with a StatefulBuilder.
You haven't included your full code, so I'm using this example taken from the docs:
await showDialog<void>(
context: context,
builder: (BuildContext context) {
int? selectedRadio = 0;
return AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int? value) {
setState(() => selectedRadio = value);
},
);
}),
);
},
),
);
},
);
See also
A YouTube video by the Flutter team explaining StatefulBuilder

Flutter Visibility widget not working third time

I have wrapped ListView.builder inside Visible widget, and the button for its visible property is in a ListTile widget with variable _currencyVisible.
The widget Visible works 2 times i.e. false/hidden(default), then changes to visible when clicked, and again hides on the second click, but it doesn't work after that. Printing on console _currencyVisible shows correct data.
Here's my code:
menuItems(BuildContext context) {
bool _currencyVisible = false;
return StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) {
return ListView(
children: [
ListTile(
title: FutureBuilder<dynamic>(
future: getUserCurrencySymbol(),
builder:(BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return Text("Currency " + snapshot.data.toString());
}),
trailing: IconButton(
icon: Icon(Icons.refresh),
onPressed: () { setState(() { _currencyVisible = !_currencyVisible; }); },
),
),
Visibility(
visible: _currencyVisible,
child: ListView.builder(
shrinkWrap: true,
itemCount:
currency.allCurrenciesList.length,
itemBuilder: (context, index) {
for (Currency c in currency.allCurrenciesList) {
currency.allCurrenciesList.removeAt(0);
return Card(
child: ListTile(
title: Text(c.country),
subtitle: Text(c.shortCurrency),
trailing: Text(c.symbol),
onTap: () {
saveUserCurrency(c.country, context);
},
),
);
}
return Text("Not Null");
},
),
),
],
);
},
);
}
You are removing all of the data from your currency list. The widget is showing correctly, but there is no data to display.
Remove this line
currency.allCurrenciesList.removeAt(0);
Don't loop through the currencies in itemBuilder. Use index instead.
Visibility(
visible: _currencyVisible,
child: ListView.builder(
shrinkWrap: true,
itemCount: currency.allCurrenciesList.length,
itemBuilder: (context, index) {
final c = currency.allCurrenciesList[index];
return Card(
child: ListTile(
title: Text(.country),
subtitle: Text(c.shortCurrency),
trailing: Text(c.symbol),
onTap: () {
saveUserCurrency(c.country, context);
},
);
}
return Text("Not Null");
),
),

Flutter application Navigation Issue

There are 2 screens in my application. In first screen I am listing all data from my sqflite database. In second screen I am giving functionality to delete that record. But when I pop that screen from the stack. It should be refreshed. How can I achieve that.
This is my first screen return code.
return FutureBuilder<List>(
future: DatabaseHelper.instance.queryAll(),
initialData: List(),
builder: (context, snapshot) {
return snapshot.hasData
? new ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: snapshot.data.length,
itemBuilder: (context, i) {
print("value : " + snapshot.data.toString());
return new Card(
child: Column(mainAxisSize: MainAxisSize.min, children: <
Widget>[
ListTile(
leading:
Image.file(File(snapshot.data[i]["thumbnail_url"])),
title: Text(
snapshot.data[i]["title"],
style: _biggerFont,
),
subtitle: Text(snapshot.data[i]["month"] +
", " +
snapshot.data[i]["year"]),
onTap: () {
},
),
ButtonBar(
children: <Widget>[
Visibility(
visible: _isUrduAvail,
child: FlatButton(
child: const Text('اردو'),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => OfflinePdfViewer(
snapshot.data[i]["id"].toString(),
snapshot.data[i]["title"],
snapshot.data[i]["urdu_url"],
"Urdu")));
},
),
),
Visibility(
visible: _isEnglishAvail,
child: FlatButton(
child: const Text('English'),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => OfflinePdfViewer(
snapshot.data[i]["id"].toString(),
snapshot.data[i]["title"],
snapshot.data[i]["english_url"],
"English")));
},
),
),
Visibility(
visible: _isHindiAvail,
child: FlatButton(
child: const Text('हिन्दी'),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => OfflinePdfViewer(
snapshot.data[i]["id"].toString(),
snapshot.data[i]["title"],
snapshot.data[i]["hindi_url"],
"Hindi")));
},
),
),
],
)
]));
},
)
: Center(
child: CircularProgressIndicator(),
);
},
);
In this screen i am building the list and populate the FutureBuilder.
and in second screen I have button on that button click the record will be deleted but what will be the route to call that it can be refreshed?
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => LibraryScreen(),
),
(route) => false,
);
I have tried this code but it clear all my activity stack.
When you call second screen try to use 'pushReplacement' instead of 'push'
If you pushAndRemoveUntil you cannot go back to the same page. You need to push it again in which case the FutureBuilder will rebuild and you will see the correct data.
Or a better solution would be to get your data as a Stream instead of a Future and use StreamBuilder instead of FutureBuilder.
You can try to use 'Pushreplacement' because it will dispose the previous route.

AlertDialog should show another AlertDialog but it's not happening till i close it and open it again

I want to show another AlertDialog when I click on one of its children
But when I click on it doesn't
Show it until I close the Alert and open it again
I want to navigate to Second AlertDialog without closing it
Any help will be appreciated
any way to make it open another dialog or a way to close it and open it again
Here is the code
padding: const EdgeInsets.only(top: 12.0),
child: ListView(children: [
FutureBuilder<DropDown>(
future: getDropData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
DropDown dropdown = snapshot.data;
return RaisedButton(
color: maincolor,
splashColor: accentcolor,
onPressed: () {
showDialog(
context: context,
useSafeArea: true,
child: Center(
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: ListView.builder(
itemCount: dropdown.categories.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(
top: 8.0, right: 8, left: 8),
child: Container(
),
child: FlatButton(
onPressed: () {
setState(() {
categoryID = dropdown
.categories[index]
.categoryId;
});
getDropData();
},
child: Text(
dropdown.categories[index].name,
)),
),
);
}),
),
));```
I think you just need to use setState() to wrap the showDialog() but I can't test it because I don't have a DropDown class.
EDIT
I tried with a simple structure and it works fine. As Dung Ngo mentioned, just use a StatefulBuilder to build the content of the first AlertDialog widget. Triggering another showDialog() inside to bring up the second AlertDialog widget.
class _YourWidgetState extends State<YourWidget> {
AlertDialog alert = AlertDialog(content: Center(child:Text("Second Alert Dialog")));
#override
Widget build(BuildContext context) {
return RaisedButton(onPressed: (){
showDialog(
context: context,
builder: (_) => AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState){
return Column(
children: <Widget>[
RaisedButton( onPressed: (){
showDialog(context: context, builder: (_) => alert);
}),
RaisedButton(onPressed: (){
showDialog(context: context, builder: (_) => alert);
}),
],
);
}
),
)
);
});
}
}
Results
The setState doesn't work until you close the first one because it belongs to the context of mainpage, not the context of your 1st dialog.
You can use a StatefulBuilder to create a a StateSetter that invoke a rebuild base on the context of the 1st dialog: https://api.flutter.dev/flutter/widgets/StatefulBuilder-class.html
Thanks to Dung Ngo and Kennith for your answers it really helped me and I learnt from your answers a lot your answers were right by the way
here what I have done
a method that opens the second dialog
showGeneralDialog(
barrierLabel: "Barrier",
barrierDismissible: true,
barrierColor: maincolor,
transitionDuration: Duration(milliseconds: 200),
context: context,
pageBuilder: (_, __, ___) {
return FutureBuilder<Manufacturer>(
future: getManufucturer(categoryID, parentID),
builder: (context, snapshot) {
if (snapshot.hasData) {
Manufacturer dropdown = snapshot.data;
return Container(
height: 400,
width: 200,
child: ListView.builder(
itemCount: dropdown.manufacturers.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 60),
child: RaisedButton(
child: Text(
dropdown.manufacturers[index].name,
style: GoogleFonts.cairo(
color: maincolor,
fontSize: 14,
fontWeight: FontWeight.bold),
),
onPressed: () async {
parentID =
dropdown.manufacturers[index].manufacturerId;
manufacturerID = parentID;
print(parentID);
Manufacturer newDropDown =
await getManufucturer(categoryID, parentID);
Navigator.pop(context);
Navigator.pop(context);
}),
);
},
),
);
} else {
return Center(
child: CircularProgressIndicator(
strokeWidth: 12,
backgroundColor: maincolor,
),
);
}
});
},
transitionBuilder: (_, anim, __, child) {
return ScaleTransition(
scale: Tween(begin: 0.0, end: 1.0).animate(anim),
child: child,
);
},
);
}
then I created the button that opens the first dialog and it that dialog I
called that method that I defined earlier
again thanks for your efforts Dung and kennith you really helped me