How to save page state on revisit in flutter - flutter

I have 2 screens and I am trying to achieve understand how to achieve page state. For example, in the below screen, I have 4 options, and all of them take the user to the same screen the only difference is API getting called for each of them is different to build a list. I am trying to handle back arrow action, and that's where I am having issues.
Use Case -
When user is on screen 2 he is playing the song, now on back press song continues to play. Now when the user again chooses the same option from screen 1 I want to show the same list without reload and selection. If user selects any other option it should behave normal, which is working.
Solution -
I can hardcode loaded songs list and send back to screen 1 and then again get that back with the selection but this will take lot of resources.
AutomaticKeepAliveClientMixin i tried this tutorial but that didn't help or kept state active.
PageStorage is 3rd option i saw but i found majority of the time this is being used on app where we have a tab.
Screen 1 -
class Dashboard extends StatefulWidget {
int playingId;
Dashboard({this.playingId});
#override
_DashboardState createState() => _DashboardState(playingId);
}
class _DashboardState extends State<Dashboard> {
String appname;
int playingId = 0;
_DashboardState(this.playingId);
// print('${playingId}');
#override
void initState() {
appname="";
// playingId=0;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: AppColors.darkBlue,
centerTitle: true,
title: Text("Tirthankar",
style: TextStyle(color: Colors.white),),
),
backgroundColor: AppColors.styleColor,
body: Column(
children: <Widget>[
// SizedBox(height: 5),
Padding(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CustomGridWidget(
child: Icon(
Icons.file_download,
size: 100,
color: AppColors.styleColor,
),
// image: 'assets/bhaktambar.png',
sizew: MediaQuery.of(context).size.width * .4,
sizeh: MediaQuery.of(context).size.width * .5,
borderWidth: 2,
label: "Bhakti",
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ListPage(appname: "Kids",playingId: playingId,),
),
);
},
),
CustomGridWidget(
child: Icon(
Icons.file_download,
size: 100,
color: AppColors.styleColor,
),
// image: 'assets/bhaktambar.png',
sizew: MediaQuery.of(context).size.width * .4,
sizeh: MediaQuery.of(context).size.width * .5,
borderWidth: 2,
label: "Kids",
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ListPage(appname: "Kids",playingId: playingId,),
),
);
},
),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 20,
right: 20,
bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CustomGridWidget(
child: Icon(
Icons.favorite,
size: 100,
color: AppColors.styleColor,
),
// image: 'assets/kids.jpg',
sizew: MediaQuery.of(context).size.width * .4,
sizeh: MediaQuery.of(context).size.width * .5,
borderWidth: 2,
label: "Favorite",
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ListPage(appname: "Songs"),
),
);
},
),
Align(
alignment: Alignment.center,
child: CustomGridWidget(
child: Icon(
Icons.book,
size: 100,
color: AppColors.styleColor,
),
// image: 'assets/vidyasagar.jpg',
sizew: MediaQuery.of(context).size.width * .4,
sizeh: MediaQuery.of(context).size.width * .5,
borderWidth: 2,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ListPage(appname: "Bhajan"),
),
);
},
),
),
],
),
),
]
),
);
}
Material boxTiles(IconData icon, String name){
return Material(
color: AppColors.mainColor,
elevation: 14.0,
shadowColor: AppColors.styleColor,
borderRadius: BorderRadius.circular(24.0),
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child:Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
InkWell(
onTap: (){print("tapped"); /* or any action you want */ },
child: Container(
width: 130.0,
height: 10.0,
color : Colors.transparent,
), // container
), //
//Text
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(name,
style:TextStyle(
color: AppColors.styleColor,
fontSize: 20.0,
)
),
),
//Icon
Material(
color: AppColors.styleColor,
borderRadius: BorderRadius.circular(50.0),
child: Padding(padding: const EdgeInsets.all(10.0),
child: Icon(icon, color: AppColors.lightBlue,size: 30,),
),
),
],
)
],
)
),
)
);
}
}
Screen 2 -
// import 'dart:js';
class ListPage extends StatefulWidget {
String appname;
int playingId;
ListPage({this.appname,this.playingId});
#override
_ListPageState createState() => _ListPageState(appname,playingId);
}
class _ListPageState extends State<ListPage>
with SingleTickerProviderStateMixin,AutomaticKeepAliveClientMixin<ListPage> {
String appname;
int playingId;
bool isPlaying = false;
_ListPageState(this.appname,playingId);
// List<MusicModel> _list1;
List<MusicData> _list;
var _value;
int _playId;
int _songId;
String _playURL;
bool _isRepeat;
bool _isShuffle;
bool _isFavorite;
String _startTime;
String _endTime;
AnimationController _controller;
Duration _duration = new Duration();
Duration _position = new Duration();
final _random = new Random();
AudioPlayer _audioPlayer = AudioPlayer();
#override
void initState() {
_playId = 0;
// _list1 = MusicModel.list;
this._fileUpdate();
// _list = _list1;
_controller =
AnimationController(vsync: this, duration: Duration(microseconds: 250));
_value = 0.0;
_startTime = "0.0";
_endTime = "0.0";
_isRepeat = false;
_isShuffle = false;
_isFavorite = false;
_audioPlayer.onAudioPositionChanged.listen((Duration duration) {
setState(() {
// _startTime = duration.toString().split(".")[0];
_startTime = duration.toString().split(".")[0];
_duration = duration;
// _position = duration.toString().split(".")[0];
});
});
_audioPlayer.onDurationChanged.listen((Duration duration) {
setState(() {
_endTime = duration.toString().split(".")[0];
_position = duration;
});
});
_audioPlayer.onPlayerCompletion.listen((event) {
setState(() {
isPlaying = false;
_position = _duration;
if (_isRepeat) {
_songId = _songId;
} else {
if (_isShuffle) {
var element = _list[_random.nextInt(_list.length)];
_songId = element.id;
} else {
_songId = _songId + 1;
}
}
_player(_songId);
});
});
super.initState();
}
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: AppColors.mainColor,
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: (){
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Dashboard(playingId: _songId),
),
);
},
),
title: Text(
appname,
style: TextStyle(color: AppColors.styleColor),
),
),
backgroundColor: AppColors.mainColor,
body: Stack(
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CustomButtonWidget(
child: Icon(
Icons.favorite,
color: AppColors.styleColor,
),
size: 50,
onTap: () {},
),
CustomButtonWidget(
image: 'assets/logo.jpg',
size: 100,
borderWidth: 5,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => DetailPage(),
),
);
},
),
CustomButtonWidget(
child: Icon(
Icons.menu,
color: AppColors.styleColor,
),
size: 50,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => HomePage(),
),
);
},
)
],
),
),
slider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(
_isRepeat ? Icons.repeat_one : Icons.repeat,
color: _isRepeat ? Colors.brown : AppColors.styleColor,
),
onPressed: () {
if (_isRepeat) {
_isRepeat = false;
} else {
_isRepeat = true;
}
},
),
IconButton(
icon: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
color: AppColors.styleColor,
),
onPressed: () {
if (isPlaying) {
_audioPlayer.pause();
setState(() {
isPlaying = false;
});
} else {
if (!isPlaying){
_audioPlayer.resume();
setState(() {
isPlaying = true;
});
}
}
}),
IconButton(
icon: Icon(
Icons.stop,
color: AppColors.styleColor,
),
onPressed: () {
if (isPlaying){
_audioPlayer.stop();
setState(() {
isPlaying = false;
_duration = new Duration();
});
}
// isPlaying = false;
}),
IconButton(
icon: Icon(
Icons.shuffle,
color:
_isShuffle ? Colors.brown : AppColors.styleColor,
),
onPressed: () {
if (_isShuffle) {
_isShuffle = false;
} else {
_isShuffle = true;
}
}),
],
),
),
Expanded(
//This is added so we can see overlay else this will be over button
child: ListView.builder(
physics:
BouncingScrollPhysics(), //This line removes the dark flash when you are at the begining or end of list menu. Just uncomment for
// itemCount: _list.length,
itemCount: _list == null ? 0 : _list.length,
padding: EdgeInsets.all(12),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
_songId = index;
_player(index);
},
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
//This below code will change the color of sected area or song being played.
decoration: BoxDecoration(
color: _list[index].id == _playId
? AppColors.activeColor
: AppColors.mainColor,
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
//End of row color change
child: Padding(
padding: const EdgeInsets.all(
16), //This will all padding around all size
child: Row(
mainAxisAlignment: MainAxisAlignment
.spaceBetween, //This will allign button to left, else button will be infront of name
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
_list[index].title,
style: TextStyle(
color: AppColors.styleColor,
fontSize: 16,
),
),
Text(
_list[index].album,
style: TextStyle(
color: AppColors.styleColor.withAlpha(90),
fontSize: 16,
),
),
],
),
IconButton(
icon: Icon(_isFavorite
? Icons.favorite
: Icons.favorite_border),
onPressed: () {
if (_isFavorite) {
_isFavorite = false;
} else {
_isFavorite = true;
}
})
//Diabled Play button and added fav button.
// CustomButtonWidget(
// //This is Play button functionality on list page.
// child: Icon(
// _list[index].id == _playId
// ? Icons.pause
// : Icons.play_arrow,
// color: _list[index].id == _playId
// ? Colors.white
// : AppColors.styleColor,
// ),
// size: 50,
// isActive: _list[index].id == _playId,
// onTap: () async {
// _songId = index;
// _player(index);
// },
// )
],
),
),
),
);
},
),
)
],
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 50,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.mainColor.withAlpha(0),
AppColors.mainColor,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
)),
),
)
],
),
// floatingActionButton: FloatingActionButton(
// child: Icon(Icons.music_note),
// onPressed: () async { // String filePath = await FilePicker.getFilePath();
// int status = await _audioPlayer.play("https://traffic.libsyn.com/voicebot/Jan_Konig_on_the_Jovo_Open_Source_Framework_for_Voice_App_Development_-_Voicebot_Podcast_Ep_56.mp3");
// if (status == 1){
// setState(() {
// isPlaying = true;
// });
// }
// },
// )
);
}
Widget slider() {
return Slider(
activeColor: AppColors.styleColor,
inactiveColor: Colors.lightBlue,
value: _duration.inSeconds.toDouble(),
min: 0.0,
max: _position.inSeconds.toDouble(),
divisions: 10,
onChangeStart: (double value) {
print('Start value is ' + value.toString());
},
onChangeEnd: (double value) {
print('Finish value is ' + value.toString());
},
onChanged: (double value) {
setState(() {
seekToSecond(value.toInt());
value = value;
});
});
}
Future<Void> _fileUpdate() async {
String url =
"azonaws.com/input.json";
String arrayObjsText = "";
try {
eos.Response response;
Dio dio = new Dio();
response = await dio.get(url,options: Options(
responseType: ResponseType.plain,
),);
arrayObjsText = response.data;
print(response.data.toString());
} catch (e) {
print(e);
}
var tagObjsJson = jsonDecode(arrayObjsText)['tags'] as List;
this.setState(() {
_list = tagObjsJson.map((tagJson) => MusicData.fromJson(tagJson)).toList();
});
// return _list;
// print(_list);
}
Future<void> _player(int index) async {
if (isPlaying) {
if (_playId == _list[index].id) {
int status = await _audioPlayer.pause();
if (status == 1) {
setState(() {
isPlaying = false;
});
}
} else {
_playId = _list[index].id;
_playURL = _list[index].songURL;
_audioPlayer.stop();
int status = await _audioPlayer.play(_playURL);
if (status == 1) {
setState(() {
isPlaying = true;
});
}
}
} else {
_playId = _list[index].id;
_playURL = _list[index].songURL;
int status = await _audioPlayer.play(_playURL);
if (status == 1) {
setState(() {
isPlaying = true;
});
}
}
}
String _printDuration(Duration duration) {
String twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
}
void seekToSecond(int second) {
Duration newDuration = Duration(seconds: second);
_audioPlayer.seek(newDuration);
}
}

PageStorage is 3rd option i saw but i found majority of the time this is being used on app where we have a tab.
You still can use this approach with what you want, with or without Tab/ bottom navigation bar.
PageViewClass
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> with SingleTickerProviderStateMixin{
PageController _pageController;
#override
void initState() {
super.initState();
_pageController = PageController();
}
#override
void dispose() {
super.dispose();
_pageController?.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: PageView(
controller: _pageController,
physics: NeverScrollableScrollPhysics(), // so the user cannot scroll, only animating when they select an option
children: <Widget>[
Dashboard(playingId: 1, key: PageStorageKey<String>('MyPlayList'), pageController: _pageController), //or the name you want, but you need to give them a key to all the child so it can save the Scroll Position
ListPage(appname: "FirstButton",playingId: 1, key: PageStorageKey<String>('FirstButton'), pageController: _pageController),
ListPage(appname: "SecondButton",playingId: 1, key: PageStorageKey<String>('SecondButton'), pageController: _pageController),
ListPage(appname: "ThirdButton",playingId: 1, key: PageStorageKey<String>('ThirdButton'), pageController: _pageController),
ListPage(appname: "FourthButton",playingId: 1, key: PageStorageKey<String>('FourthButton'), pageController: _pageController)
],
),
)
);
}
}
Now you pass the PageController to all children (add a key and PageController attribute to Screen 1 and 2) and in DashBoard (Screen 1) you can change the onTap of the buttons from the MaterialRoute to this
onTap: () => widget.pageController.jumpToPage(x), //where x is the index of the children of the PageView you want to see (between 0 and 4 in this case)
In Screen 2 you wrap all your Scaffold with a WillPopScope so when the user tap back instead of closing the route it goes back to the Dashboard Widget at index 0
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
widget.pageController.jumpToPage(0); // Move back to dashboard (index 0)
return false;
}
child: Scaffold(...)
);
}
You can use other methods of PageController if you want some effects like animations (animateTo, nextPage, previousPage, etc.)

Related

Toggle switch animation not working in flutter

`I am trying to implement a toggle switch in which when I toggle the switch I display two different things but I'm getting an error while I use setstate with my logic or something like that if I remove the setstate from my code the animation starts working again but the logic does not work and I don't get two different outcomes when i toggle between the switch
the code is :
import 'package:flutter/material.dart';
import 'package:sadapay_clone/screens/homepage.dart';
import 'package:sadapay_clone/widgets/physical_card_item.dart';
import 'package:sadapay_clone/widgets/virtual_card_item.dart';
import 'package:toggle_switch/toggle_switch.dart';
class CardScreen extends StatefulWidget {
const CardScreen({super.key});
#override
State<CardScreen> createState() => _CardScreenState();
}
class _CardScreenState extends State<CardScreen>{
// AnimationController _controller;
bool toggle = false;
// #override
// void initState() {
// _controller = AnimationController(vsync: this);
// super.initState();
// }
// void toggleSwitch(int index) {
// if (index == 0) {
// setState(() {
// toggle = true;
// });
// } else if (index == 1) {
// setState(() {
// toggle = false;
// });
// }
// }
void toggleSwitch(int index) {
if (index == 0) {
setState(() {
toggle = true;
});
} else if (index == 1) {
setState(() {
toggle = false;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
const SizedBox(height: 75),
SizedBox(
width: double.infinity,
child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: () {
Navigator.pop(
context,
MaterialPageRoute(
builder: (context) => const MyHomePage(),
),
);
},
icon: const Icon(Icons.arrow_back_ios),
),
Container(
width: 295,
alignment: Alignment.center,
child: const Text(
'My Cards',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
],
),
),
const SizedBox(
height: 20,
),
Container(
alignment: Alignment.center,
height: 40,
width: 365,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.grey[300],
),
child: ToggleSwitch(
minHeight: 30,
minWidth: 180.0,
cornerRadius: 20.0,
activeBgColors: const [
[Colors.white],
[Colors.white]
],
activeFgColor: Colors.black,
inactiveBgColor: Colors.grey[300],
inactiveFgColor: Colors.black54,
initialLabelIndex: 0,
totalSwitches: 2,
labels: const ['Virtual', 'Physical'],
radiusStyle: true,
onToggle: (index) {
toggleSwitch(index!);
},
// onToggle: (index) {
// setState(() {
// toggle = index == 0;
// });
// },
),
),
toggle ? VirtualCard() : PhysicalCard(),
],
),
);
}
}
I tried using setstate logic inside the function rather than using it inside the onchanged property but still, the logic was working I was seeing two different outcomes when I pressed the switch but the animation was not working`
The issue is, while we are calling setState, the build method is trigger and setting initialLabelIndex to 0 again, you can do a check here,
class _CardScreenState extends State<CardScreen> {
bool toggle = false;
void toggleSwitch(int index) {
if (index == 0) {
setState(() {
toggle = true;
});
} else if (index == 1) {
setState(() {
toggle = false;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
alignment: Alignment.center,
height: 40,
width: 365,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.grey[300],
),
child: ToggleSwitch(
minHeight: 30,
minWidth: 180.0,
cornerRadius: 20.0,
activeBgColors: const [
[Colors.white],
[Colors.white]
],
activeFgColor: Colors.black,
inactiveBgColor: Colors.grey[300],
inactiveFgColor: Colors.black54,
initialLabelIndex: toggle ? 0 : 1,
totalSwitches: 2,
labels: ['Virtual', 'Physical'],
radiusStyle: true,
onToggle: (index) {
toggleSwitch(index!);
},
),
),
toggle ? Text("VirtualCard") : Text("PhysicalCard"),
],
),
);
}
}

I dont want to display all the records of table in listview but I want to display records of only particular customer in flutter

I have a screen which shows list of customers using listview. Next when I click on a customer I want to show the notes(records) only of that particular customer(customerId) in next screen in listview. I know how to display all the records of table. but how to display only certain records?? Neither there is any error nor the listview is being displayed. I dont know where am I going wrong.
List<CustomerNote> cnoteList;
int count = 0;
if (cnoteList == null) {
cnoteList = List<CustomerNote>();
updateCustomerNotes();
}
db_service.dart
Future<List<CustomerNote>> getCustomerNotes(int customer) async {
await DB.init();
var res = await DB.rawQuery("noteDetails WHERE custId = '$customer'");
int count = res.length;
List<CustomerNote> cnotelist = List<CustomerNote>();
for (int i = 0; i < count; i++) {
cnotelist.add(CustomerNote.fromMap(res[i]));
}
return cnotelist;
}
People_List.dart //this is the file which displays list of customers
import 'package:customer/models/Note.dart';
import 'package:customer/models/addCustomer.dart';
import 'package:customer/screens/New_Note.dart';
import 'package:customer/screens/Note_info.dart';
import 'package:customer/screens/User_Settings.dart';
import 'package:customer/screens/add_person.dart';
import 'package:customer/services/db_service.dart';
import 'package:customer/utils/database_helper.dart';
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:sqflite/sqflite.dart';
class People_List extends StatefulWidget{
#override
State<StatefulWidget> createState() {
return People_ListState();
}
}
class People_ListState extends State<People_List> with SingleTickerProviderStateMixin{
DBService dbService = DBService();
List<AddCustomer> customerList;
int count = 0;
static final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
var _isSelectedItemIndex;
TextEditingController _searchQuery;
bool _isSearching = false;
String searchQuery = "Search query";
#override
void initState() {
super.initState();
_searchQuery = new TextEditingController();
}
void _startSearch() {
print("open search box");
ModalRoute
.of(context)
.addLocalHistoryEntry(new LocalHistoryEntry(onRemove: _stopSearching));
setState(() {
_isSearching = true;
});
}
void _stopSearching() {
_clearSearchQuery();
setState(() {
_isSearching = false;
});
}
void _clearSearchQuery() {
print("close search box");
setState(() {
_searchQuery.clear();
updateSearchQuery("Search query");
});
}
Widget _buildTitle(BuildContext context) {
var horizontalTitleAlignment =
Platform.isIOS ? CrossAxisAlignment.center : CrossAxisAlignment.start;
return new InkWell(
onTap: () => scaffoldKey.currentState.openDrawer(),
child: new Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: horizontalTitleAlignment,
children: <Widget>[
const Text(''),
],
),
),
);
}
Widget _buildSearchField() {
return new TextField(
controller: _searchQuery,
autofocus: true,
decoration: const InputDecoration(
hintText: 'Search...',
border: InputBorder.none,
hintStyle: const TextStyle(color: Colors.white30),
),
style: const TextStyle(color: Colors.white, fontSize: 16.0),
onChanged: updateSearchQuery,
);
}
void updateSearchQuery(String newQuery) {
setState(() {
searchQuery = newQuery;
});
print("search query " + newQuery);
}
List<Widget> _buildActions() {
if (_isSearching) {
return <Widget>[
new IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
if (_searchQuery == null || _searchQuery.text.isEmpty) {
Navigator.pop(context);
return;
}
_clearSearchQuery();
},
),
];
}
return <Widget>[
new IconButton(
icon: const Icon(Icons.search),
onPressed: _startSearch,
),
];
}
#override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
if (customerList == null) {
customerList = List<AddCustomer>();
updateListView();
}
return Scaffold(
appBar: new AppBar(
leading: _isSearching ? const BackButton() : null,
title: _isSearching ? _buildSearchField() : _buildTitle(context),
actions: _buildActions(),
),
body:getCustomerListView(),
floatingActionButton: FloatingActionButton(
onPressed: () {
navigateToCustomer(AddCustomer(), 'Add Person');
},
child: const Icon(Icons.add),
),
bottomNavigationBar:Row (
children: <Widget>[
buildNavBarItem(Icons.home,0,"Home"),
buildNavBarItem(Icons.fiber_new_rounded,1,"Upcoming"),
buildNavBarItem(Icons.history,2,"History"),
buildNavBarItem(Icons.account_circle,3,"Account"),
],
),
);
}
ListView getCustomerListView() {
TextStyle titleStyle = Theme.of(context).textTheme.subhead;
return ListView.builder(
itemCount: count,
itemBuilder: (BuildContext context, int position) {
return Card(
color: Colors.white,
elevation: 2.0,
child: ListTile(
/*trailing: CircleAvatar(
backgroundColor: getPriorityColor(this.noteList[position].priority),
child: getPriorityIcon(this.noteList[position].priority),
),*/
title: Text(this.customerList[position].custName, style: titleStyle,),
//subtitle: Text(this.customerList[position].date),
onTap: () {
},
),
);
},
);
}
void navigateToCustomer(AddCustomer customer, String title) async {
bool result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
return AddPerson(customer, title);
}));
if (result == true) {
updateListView();
}
}
void updateListView() {
final Future<Database> dbFuture = DB.init();
dbFuture.then((database) {
Future<List<AddCustomer>> customerListFuture = dbService.getCustomerList();
customerListFuture.then((customerList) {
setState(() {
this.customerList = customerList;
this.count = customerList.length;
});
});
});
}
Widget buildNavBarItem(IconData icon,int index,title) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
return GestureDetector(
onTap: () {
setState(() {
_isSelectedItemIndex=index;
if(_isSelectedItemIndex==3) {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => User_Settings()));
}
});
},
child: Container(
height: height * 0.09,
width: width / 4,
decoration: index==_isSelectedItemIndex?BoxDecoration(
border: Border(
bottom: BorderSide(width: 4,color: Theme.of(context).primaryColor),
),
gradient: LinearGradient(colors: [
Theme.of(context).primaryColor.withOpacity(0.3),
Theme.of(context).primaryColor.withOpacity(0.015),
],begin: Alignment.bottomCenter,end: Alignment.topCenter)
):BoxDecoration(),
child: Column(
children: <Widget>[
InkWell(
child: Icon(icon,
color: index==_isSelectedItemIndex? Theme.of(context).primaryColor:Colors.black54,
),
),
Text(title)
],)),
);
}
}
Note_Info.dart
class Note_Info extends StatefulWidget{
final String appBarTitle;
final AddCustomer customer;
Note_Info(this. customer, this.appBarTitle);
#override
State<StatefulWidget> createState() {
return Note_InfoState(this.customer,this.appBarTitle);
}
}
class Note_InfoState extends State<Note_Info> {
CustomerNote note=CustomerNote();
DBService dbService = DBService();
List<CustomerNote> cnoteList;
int count = 0;
String appBarTitle;
AddCustomer customer;
Note_InfoState(this.customer, this.appBarTitle);
PickedFile _imageFile;
final ImagePicker _picker = ImagePicker();
bool rememberMe = false;
DateTime _date = DateTime.now();
TextEditingController custNameController = TextEditingController();
void getImage(ImageSource source) async {
final pickedFile = await _picker.getImage(
source: source);
setState(() {
_imageFile = pickedFile;
});
}
#override
Widget build(BuildContext context) {
if (noteList == null) {
noteList = List<CustomerNote>();
updateCustomerNotes();
}
var height = MediaQuery.of(context).size.height;
custNameController.text = customer.custName;
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(
Icons.add,
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => NewNote()));
},
)
],
),
body: Container(
child: Column(
children: <Widget>[
TextField(controller: custNameController,
style: TextStyle(
fontSize: 20.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center),
Padding(
padding: const EdgeInsets.all(15.0),
child: Row(children: [
ImageProfile(),
Padding(
padding: const EdgeInsets.only(left: 30.0),
child: IconButton(
icon: Icon(
Icons.call,
color: Colors.green,
size: 45,
),
onPressed: () {
},
),
),
],),
),
SizedBox(
height: 50,
child: AppBar(
bottom: TabBar(
tabs: [
Tab(
text: "All",
),
Tab(
text: "Pending",
),
Tab(
text: "Cancelled",
),
Tab(
text: "Completed",
),
],
),
),
),
// create widgets for each tab bar here
Expanded(
child: TabBarView(
children: [
// first tab bar view widget
Container(
child: getNoteListView(),
),
// second tab bar view widget
Container(
child: Center(
child: Text(
'Pending Items',
),
),
),
Container(
child: Center(
child: Text(
'Cancelled',
),
),
),
Container(
child: Center(
child: Text(
'Completed',
),
),
),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 55.0,
width: 200,
child: RaisedButton(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
color: Theme
.of(context)
.primaryColorDark,
textColor: Colors.white,
child: Text('Save', textScaleFactor: 1.5,),
onPressed: () {
setState(() {
//_reset();
});
},
),
),
),
]
),
)
));
}
void updateCustomerNotes() {
final Future<Database> dbFuture = DB.init();
dbFuture.then((database) {
int customerId=customer.custId;
Future<List<CustomerNote>> noteListFuture = dbService.getCustomerNotes(customerId);
noteListFuture.then((cnoteList) {
setState(() {
this.cnoteList = cnoteList;
//this.count = cnoteList.length;
});
});
});
}
ListView getNoteListView() {
TextStyle titleStyle = Theme.of(context).textTheme.subhead;
return ListView.builder(
itemCount: count,
itemBuilder: (BuildContext context, int position) {
return Card(
color: Colors.white,
elevation: 2.0,
child: ListTile(
title: Text(this.cnoteList[position].note, style: titleStyle,),
subtitle: Text(this.cnoteList[position].date),
trailing: GestureDetector(
child: Icon(Icons.delete, color: Colors.grey,),
onTap: () {
},
),
onTap: () {
},
),
);
},
);
}
void _showSnackBar(BuildContext context, String message) {
final snackBar = SnackBar(content: Text(message));
Scaffold.of(context).showSnackBar(snackBar);
}
Widget ImageProfile() {
return Center(
child: CircleAvatar(
radius: 80.0,
backgroundImage: AssetImage('images/person_icon.jpg')
),
);
}
}
If u want to get some specific data from the server then you need to provide (where: ) in your query such as
List<Map> result = await db.query(
DatabaseHelper.table,
columns: columnsToSelect,
where: whereString,
whereArgs: whereArguments);
// print the results
result.forEach((row) => print(row));
// {_id: 1, name: Bob, age: 23}
}
Note your query can be different from the above one.
and if you want to get the same specific data from already fetched data then also you need to use the where clause such as
final outData = responseData.where((i) => i.userId == customerId).toList();
and then pass the outData to next page through params.

Flutter Cupertino Picker errors out on first item if picker isn't scrolled

The app I'm building uses a Cupertino Picker that shows a list of items to select, in this case the names of the US States. The first item defaults to the first item in the list ('ak'), when the button to select the item is pressed, the app errors out. This only happens with the first item, when the picker isn't scrolled. If the picker is scrolled and the user goes back to the first item, it works fine.
class StateSelectScreen extends StatefulWidget {
static const String id = 'state_select_screen';
#override
_StateSelectScreenState createState() => _StateSelectScreenState();
}
class _StateSelectScreenState extends State<StateSelectScreen> {
String selectedState = 'ak';
bool showSpinner = false;
DropdownButton<String> androidDropdown() {
List<DropdownMenuItem<String>> dropdownItems = [];
for (String state in statesList) {
var newItem = DropdownMenuItem(
child: Text(
USStates.getName(state).toUpperCase(),
textAlign: TextAlign.center,
),
value: state,
);
dropdownItems.add(newItem);
}
return DropdownButton<String>(
dropdownColor: Colors.black26,
autofocus: true,
focusColor: Colors.black26,
style: TextStyle(
fontSize: k30PointFont,
),
value: selectedState,
items: dropdownItems,
onChanged: (value) {
setState(() {
selectedState = value;
getStateData();
});
},
);
}
CupertinoPicker iOSPicker() {
List<Text> pickerItems = [];
for (String state in statesList) {
pickerItems.add(Text(USStates.getName(state.toUpperCase())));
}
return CupertinoPicker(
backgroundColor: kCupertinoPickerBackgroundColor,
itemExtent: kCupertinoPickerItemExtent,
onSelectedItemChanged: (selectedIndex) {
setState(() {
selectedState = USStates.getName(statesList[selectedIndex]);
getStateData();
});
},
children: pickerItems,
);
}
Map<String, dynamic> selectedStateData = {};
bool isWaiting = false;
void getStateData() async {
isWaiting = true;
try {
var stateData = await GetData().getStateData(selectedState);
isWaiting = false;
setState(() {
selectedStateData = stateData;
});
} catch (e) {
print(e);
}
}
#override
void initState() {
super.initState();
getStateData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(kAppBarTitle),
),
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Flexible(
child: Padding(
padding: EdgeInsets.only(top: kStateSelectScreenFlexEdgeInsetsTop, bottom: kStateSelectScreenFlexEdgeBottom),
child: Hero(
tag: kHeroTag,
child: Container(
height: 200.0,
child: Image.asset(kHeroImageAsset),
),
),
),
),
Container(
child: Column(
children: <Widget>[
Container(
height: kStateSelectScreenContainerHeight,
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: kStateSelectScreenContainerPaddingBottom),
child: Platform.isIOS ? iOSPicker() : androidDropdown(),
),
BottomButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ResultsScreen(
covidData: selectedStateData,
location: selectedState,
);
},
),
);
},
buttonTitle: kCheckStateResultsButtonTitle,
),
SizedBox(
height: kHeight15,
),
BottomButton(
onPressed: () {
Navigator.pushNamed(context, MenuScreen.id);
},
buttonTitle: kMainMenuButtonTitle,
),
],
),
),
],
),
),
);
}
}
I have recreated your problem, but don't see any error.
https://codepen.io/flakerimi/pen/poRywLZ
You said when the button to select the item is pressed, the app errors out.
Which button ? I don't see any
I have added scrollController: FixedExtentScrollController(initialItem: 1), but I don't think thats the case.
return CupertinoPicker(
scrollController: FixedExtentScrollController(initialItem: 1),
backgroundColor: Colors.white,
itemExtent: 30,
onSelectedItemChanged: (selectedIndex) {
setState(() {
selectedState = statesList[selectedIndex];
print(selectedState);
});
},
children: pickerItems,
);

Flutter A RenderFlex overflowed by 99469 pixels on the bottom

I am new to flutter and trying to solve the below issue, in the first screenshot I have labelStart line 127 value as 1.21, when i pass that value using the variable i get renderflex error, if i am using hardcoded i don't get any error. Not sure what i am doing wrong. I tried adding Expanded after reading few posts online but that didn't help. Can someone please guide me on what mistake i am doing here?
class _ListPageState extends State<ListPage> with SingleTickerProviderStateMixin {
List<MusicModel> _list;
var _value;
int _playId;
String _playURL;
bool isPlaying = false;
String _startTime;
String _endTime;
AnimationController _controller;
AudioPlayer _audioPlayer = AudioPlayer();
#override
void initState() {
_playId = 0;
_list = MusicModel.list;
_controller = AnimationController(vsync: this,duration: Duration(microseconds: 250));
_value = 0.0;
_startTime="0.0";
_endTime="0.0";
super.initState();
_audioPlayer.onAudioPositionChanged.listen((Duration duration) {
setState(() {
_startTime = duration.toString().split(".")[0];
});
});
_audioPlayer.onDurationChanged.listen((Duration duration) {
setState(() {
_endTime = duration.toString().split(".")[0];
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: AppColors.mainColor,
centerTitle: true,
title: Text("Skin - Flume",
style: TextStyle(color: AppColors.styleColor),),
),
backgroundColor: AppColors.mainColor,
body: Stack(
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CustomButtonWidget(
child: Icon(
Icons.favorite,
color: AppColors.styleColor,
),
size: 50,
onTap: (){
},
),
CustomButtonWidget(
image: 'assets/logo.jpg',
size: 100,
borderWidth: 5,
onTap: (){
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => DetailPage(),
),
);
},
),
CustomButtonWidget(
child: Icon(
Icons.menu,
color: AppColors.styleColor,
),
size: 50,
onTap: (){
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => HomePage(),
),
);
},
)
],
),
),
//Progress bar section
// Expanded(child: SizedBox()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: CustomProgressWidget(
value: _value,
labelStart: "1.21",
labelend: "2.34",
),
),
// Expanded(child: SizedBox()),
Expanded( //This is added so we can see overlay else this will be over button
child: ListView.builder(
physics: BouncingScrollPhysics(),//This line removes the dark flash when you are at the begining or end of list menu. Just uncomment for
itemCount: _list.length,
padding: EdgeInsets.all(12),
itemBuilder: (context,index){
return GestureDetector(
onTap: (){
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => DetailPage(),
),
);
},
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
//This below code will change the color of sected area or song being played.
decoration: BoxDecoration(
color: _list[index].id == _playId
? AppColors.activeColor
: AppColors.mainColor,
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
//End of row color change
child: Padding(
padding: const EdgeInsets.all(16), //This will all padding around all size
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, //This will allign button to left, else button will be infront of name
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
_list[index].title,
style: TextStyle(
color: AppColors.styleColor,
fontSize: 16,
),
),
Text(
_list[index].album,
style: TextStyle(
color: AppColors.styleColor.withAlpha(90),
fontSize: 16,
),
),
],
),
CustomButtonWidget( //This is Play button functionality on list page.
child: Icon(
_list[index].id == _playId
? Icons.pause
: Icons.play_arrow,
color: _list[index].id == _playId
? Colors.white
: AppColors.styleColor,
),
size: 50,
isActive: _list[index].id == _playId,
onTap: () async {
if (isPlaying){
if (_playId == _list[index].id){
int status = await _audioPlayer.pause();
if (status == 1){
setState(() {
isPlaying = false;
});
}
} else {
_playId = _list[index].id;
_playURL = _list[index].songURL;
_audioPlayer.stop();
int status = await _audioPlayer.play(_playURL);
if (status == 1){
setState(() {
isPlaying = true;
});
}
}
// int status = await _audioPlayer.pause();
// if (status == 1){
// setState(() {
// isPlaying = false;
// });
// }
} else {
_playId = _list[index].id;
_playURL = _list[index].songURL;
int status = await _audioPlayer.play(_playURL);
if (status == 1){
setState(() {
isPlaying = true;
});
}
} // String filePath = await FilePicker.getFilePath();
},
// onTap: (){
// setState(() {
// _playId = _list[index].id;
// });
// },
)
],
),
),
),
);
},
),
)
],
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 50,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.mainColor.withAlpha(0),
AppColors.mainColor,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
)
),
),
)
],
),
);
}
}
After changing line 1.27 with a dynamic value variable.
You have to wrap you Column in a scrollview like SingleChildScrollView or ListView. This error occurs when the items in column take more space than available.
I had the same problem, and my problem was my variable extracted of my state it took too long in show and showed first the widget after the variable.
In my case use Getx, and fixed for this way:
My old code:
GetX<UserController>(
builder: (_){
return Text(_.user.name);
}
},
),
My new code
GetX<UserController>(
builder: (_){
if(_.user.email != null){
return Text(_.user.name);
}else{
return Text('Loading....');
}
},
),

How to implement a network data fetch and then display in checkbox in alert inside showDialog with setState

I couldn't find a way to fetch data from network API inside showDialog > StatefulBuilder > AlertDialog. After fetching, this data should display in checkboxes and then finally on click ok, the selected checkboxes data is returned to the parent widget. There are more states other than these checkbox states in the alert. But the Navigator.of(context).pop() can return only single value.
Is there a way to rebuild the StatefulBuilder with setState on parent widget. Or some easy hack to rebuild the StatefulBuilder from an outside function like fetchOrderStatus() in the below code. (might be possible with a key on StatefulBuilder, but don't know how).
Below is my code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:recase/recase.dart';
import 'package:woocommerceadmin/src/orders/widgets/OrderDetailsPage.dart';
import 'package:barcode_scan/barcode_scan.dart';
class OrdersListPage extends StatefulWidget {
final String baseurl;
final String username;
final String password;
OrdersListPage({
Key key,
#required this.baseurl,
#required this.username,
#required this.password,
}) : super(key: key);
#override
_OrdersListPageState createState() => _OrdersListPageState();
}
class _OrdersListPageState extends State<OrdersListPage> {
String baseurl;
String username;
String password;
List ordersListData = List();
int page = 1;
bool hasMoreToLoad = true;
bool isListLoading = false;
bool isSearching = false;
String searchValue = "";
String sortOrderByValue = "date";
String sortOrderValue = "desc";
bool isOrderStatusOptionsReady = false;
bool isOrderStatusOptionsError = false;
String orderStatusOptionsError;
Map<String, bool> orderStatusOptions = {};
final scaffoldKey = new GlobalKey<ScaffoldState>();
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
new GlobalKey<RefreshIndicatorState>();
#override
void initState() {
super.initState();
baseurl = widget.baseurl;
username = widget.username;
password = widget.password;
fetchOrdersList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
appBar: _myAppBar(),
body: RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: handleRefresh,
child: Column(
children: <Widget>[
Expanded(
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (hasMoreToLoad &&
!isListLoading &&
scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent) {
handleLoadMore();
}
},
child: ListView.builder(
itemCount: ordersListData.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OrderDetailsPage(
id: ordersListData[index]["id"],
),
),
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
_orderDate(ordersListData[index]),
_orderIdAndBillingName(
ordersListData[index]),
_orderStatus(ordersListData[index]),
_orderTotal(ordersListData[index])
]),
),
)
]),
),
);
}),
),
),
if (isListLoading)
Container(
height: 60.0,
color: Colors.white,
child: Center(
child: SpinKitFadingCube(
color: Colors.purple,
size: 30.0,
)),
),
],
),
),
);
}
Future<void> fetchOrderStatus() async {
String url =
"$baseurl/wp-json/wc/v3/reports/orders/totals?consumer_key=$username&consumer_secret=$password";
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = false;
});
dynamic response;
try {
response = await http.get(url);
if (response.statusCode == 200) {
if (json.decode(response.body) is List &&
!json.decode(response.body).isEmpty) {
json.decode(response.body).forEach((item) {
if (item is Map) {
orderStatusOptions.putIfAbsent(item["slug"], () => false);
}
});
setState(() {
isOrderStatusOptionsReady = true;
});
} else {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError = "Failed to fetch order status options";
});
}
} else {
String errorCode = "";
if (json.decode(response.body) is Map &&
json.decode(response.body).containsKey("code") &&
json.decode(response.body)["code"] is String) {
errorCode = json.decode(response.body)["code"];
}
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $errorCode";
});
}
} catch (e) {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $e";
});
}
}
Widget _myAppBar() {
Widget myAppBar;
myAppBar = AppBar(
title: Text("Orders List"),
actions: <Widget>[
GestureDetector(
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: Icon(Icons.search),
),
onTap: () {
setState(() {
isSearching = !isSearching;
});
},
),
GestureDetector(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Icon(Icons.filter_list),
),
onTap: _orderFilter,
),
],
);
}
return myAppBar;
}
void _orderFilter() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
// fetchOrderStatus();
return StatefulBuilder(builder: (context, StateSetter setState) {
return AlertDialog(
title: Text("Sort & Filter"),
titlePadding: EdgeInsets.fromLTRB(15, 20, 15, 0),
content: Container(
width: 300,
height: 400,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Sort by",
style: Theme.of(context).textTheme.subhead,
),
Row(
children: <Widget>[
Expanded(
child: Container(
child: DropdownButton<String>(
underline: SizedBox.shrink(),
value: sortOrderByValue,
onChanged: (String newValue) {
FocusScope.of(context)
.requestFocus(FocusNode());
setState(() {
sortOrderByValue = newValue;
});
},
items: <String>[
"date",
"id",
"title",
"slug",
"include"
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value.titleCase,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.body1,
),
);
}).toList(),
),
),
),
InkWell(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_downward,
color: (sortOrderValue == "desc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "desc";
});
},
),
InkWell(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_upward,
color: (sortOrderValue == "asc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "asc";
});
},
),
],
),
Text(
"Filter by",
style: Theme.of(context).textTheme.subhead,
),
Text(
"Order Status",
style: Theme.of(context).textTheme.body1.copyWith(
fontWeight: FontWeight.bold, fontSize: 16),
),
isOrderStatusOptionsReady
? ListView(
children:
orderStatusOptions.keys.map((String key) {
return new CheckboxListTile(
title: Text(key),
value: orderStatusOptions[key],
onChanged: (bool value) {
setState(() {
orderStatusOptions[key] = value;
});
},
);
}).toList(),
)
: Container(
child: Center(
child: SpinKitFadingCube(
color: Theme.of(context).primaryColor,
size: 30.0,
),
),
)
],
),
),
),
contentPadding: EdgeInsets.fromLTRB(15, 10, 15, 0),
actions: <Widget>[
FlatButton(
child: Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
});
}
}
You have a couple of options
Fetch the data before showing the dialog. Using either async/await keywords or .then, you wait for the fetching of data to complete, then use the data in the dialog
void _orderFilter() async {
await fetchOrderStatus();
showDialog(...); //Use the response in the dialog
}
Create a new stateful widget for the dialog and have fetchOrderStatus() be a method in that class. This allows you to have more control over what to display as well as state changes in the dialog.
#gaurav-jain I have followed your question from Github where you asked a question about my answer to this problem. How I did it is i have a button that when clicked immediately opens the dialog that then waits for the Future to load data from the API: The async function _showOptions() renders the dialog that renders a list of checkboxes with options fetched from API:
new RaisedButton(
color: Colors.green,
padding: EdgeInsets.all(20.0),
onPressed: () {
if (state._isLoading){
// don't do anything when form is submitting and this button is pressed again
return null;
}
else {
if (state._formKey.currentState.validate()) {
state._showOptions().then((selected){
print(state.selectedOptions);
if (state.selectedOptions.isNotEmpty) {
_submitForm();
}
else {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text('You have not selected any option'),
actions: <Widget>[
FlatButton(
child: Text('OK'),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
),
],
);
}
);
}
});
}
else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Your form has errors. Rectify them and submit again'),
)
);
}
}
},
child: Text(state._isLoading ? 'Submitting...' : 'Submit', style: new TextStyle(color: Colors.white)),
),
The state here is the parent widget's state, but the async dialog has it's own state to make it work. You can refer back to the github comment for the other pieces of the code here https://github.com/flutter/flutter/issues/15194#issuecomment-450490409
As suggested by #wxker, 2nd approach, I have implemented other stateful widget which returns AlertDialog.
Parent Widget Calling showDialog on tap:
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return OrdersListFiltersModal(
baseurl: widget.baseurl,
username: widget.username,
password: widget.password,
sortOrderByValue: sortOrderByValue,
sortOrderValue: sortOrderValue,
orderStatusOptions: orderStatusOptions,
onSubmit:
(sortOrderByValue, sortOrderValue, orderStatusOptions) {
setState(() {
this.sortOrderByValue = sortOrderByValue;
this.sortOrderValue = sortOrderValue;
this.orderStatusOptions = orderStatusOptions;
});
handleRefresh();
},
);
},
);
},
),
Child stateful widget returning alert with default value and function callback in constructor to change parent widget state.
Widget build(BuildContext context) {
return AlertDialog(
title: Text("Sort & Filter"),
titlePadding: EdgeInsets.fromLTRB(15, 20, 15, 0),
content: Container(
height: 400,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Sort by",
style: Theme.of(context).textTheme.subhead,
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
child: DropdownButton<String>(
underline: SizedBox.shrink(),
value: sortOrderByValue,
onChanged: (String newValue) {
FocusScope.of(context).requestFocus(FocusNode());
setState(() {
sortOrderByValue = newValue;
});
},
items: <String>[
"date",
"id",
"title",
"slug",
"include"
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value.titleCase,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.body1,
),
);
}).toList(),
),
),
),
InkWell(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_downward,
color: (sortOrderValue == "desc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "desc";
});
},
),
InkWell(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.arrow_upward,
color: (sortOrderValue == "asc")
? Theme.of(context).primaryColor
: Colors.black,
),
),
onTap: () {
setState(() {
sortOrderValue = "asc";
});
},
),
],
),
),
Text(
"Filter by",
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
"Order Status",
style: Theme.of(context)
.textTheme
.body1
.copyWith(fontWeight: FontWeight.bold, fontSize: 16),
),
),
SizedBox(
height: 10,
),
isOrderStatusOptionsError
? Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Text(
orderStatusOptionsError,
style: Theme.of(context).textTheme.body1,
),
),
),
],
)
: isOrderStatusOptionsReady
? Column(
children: orderStatusOptions.keys.map((String key) {
return GestureDetector(
onTap: () {
setState(() {
orderStatusOptions[key] =
!orderStatusOptions[key];
});
},
child: Container(
color: Colors.transparent,
height: 30,
child: Row(
children: <Widget>[
Checkbox(
value: orderStatusOptions[key],
onChanged: (bool value) {
setState(() {
orderStatusOptions[key] = value;
});
},
),
Expanded(
child: Text(
key.titleCase,
style:
Theme.of(context).textTheme.body1,
),
),
],
),
),
);
}).toList(),
)
: Container(
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Center(
child: SpinKitPulse(
color: Theme.of(context).primaryColor,
size: 50,
),
),
)
],
),
),
),
contentPadding: EdgeInsets.fromLTRB(15, 10, 15, 0),
actions: <Widget>[
FlatButton(
child: Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Ok"),
onPressed: () {
widget.onSubmit(
sortOrderByValue, sortOrderValue, orderStatusOptions);
Navigator.of(context).pop();
},
)
],
);
}
Future<void> fetchOrderStatusOptions() async {
String url =
"${widget.baseurl}?consumer_key=${widget.username}&consumer_secret=${widget.password}";
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = false;
});
http.Response response;
try {
response = await http.get(url);
if (response.statusCode == 200) {
if (json.decode(response.body) is List &&
!json.decode(response.body).isEmpty) {
Map<String, bool> tempMap = orderStatusOptions;
json.decode(response.body).forEach((item) {
if (item is Map &&
item.containsKey("slug") &&
item["slug"] is String &&
item["slug"].isNotEmpty) {
tempMap.putIfAbsent(item["slug"], () => false);
}
});
setState(() {
isOrderStatusOptionsReady = true;
orderStatusOptions = tempMap;
});
} else {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError = "Failed to fetch order status options";
});
}
} else {
String errorCode = "";
if (json.decode(response.body) is Map &&
json.decode(response.body).containsKey("code") &&
json.decode(response.body)["code"] is String) {
errorCode = json.decode(response.body)["code"];
}
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $errorCode";
});
}
} catch (e) {
setState(() {
isOrderStatusOptionsReady = false;
isOrderStatusOptionsError = true;
orderStatusOptionsError =
"Failed to fetch order status options. Error: $e";
});
}
}
}