RangeError : RangeError(index) : invalid value : only valid is 0: 3 - flutter

I display the products available in my basket in this Screen Basket which works very well. I use a dismissible to be able to delete an item from my basket. And I often get the error from time to time: RangeError: RangeError (index): invalid value: only valid is 0: 3. What to do please?
Here is the interface of my basket. Basket_Screen. can flutter clean be the best solution? I'm afraid to try it. What exactly does flutter clean do? I need a solution please
class PanierScreen extends StatefulWidget {
#override
_PanierScreenState createState() => _PanierScreenState();
}
class _PanierScreenState extends State<PanierScreen> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
List<Produit> produits = Provider.of<Panier>(context).produits ;
Panier _panier = Provider.of<Panier>(context, listen : false);
super.build(context);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Panier",
home: Scaffold(
appBar: AppBar(
title: Text("Panier"),
centerTitle: true,
),
body: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(child: ListView.separated(
itemCount: produits.length,
itemBuilder: (BuildContext context, int index) {
return Dismissible(
key: UniqueKey(),
direction: DismissDirection.endToStart,
background: new Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: Icon(
Icons.delete, color: Colors.white,),
),
Spacer(),
Text("Supprimer", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),)
],
),
color: Colors.red,
),
onDismissed: (direction) {
// _panier.retirerProduit(produits[index]);
produits.removeAt(index);
print(produits.length);
Scaffold.of(context).showSnackBar(new SnackBar(
content: new Text("Produit supprimé du panier"),
duration: Duration(seconds: 3),)); },
child: Card (
child: ListTile(
leading: CircleAvatar(
child: FittedBox(
child: Text("${produits[index].prixvente} FCFA"),
),
),
title: Text("${produits[index].designation}".toUpperCase(), style: TextStyle(fontWeight: FontWeight.bold),),
subtitle: Text("Total : ${produits[index].quantite_vendue * produits[index].prixvente} FCFA", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.red[300]), ),
trailing: Text("${produits[index].quantite_vendue.toString()} x", style: TextStyle(fontWeight: FontWeight.bold),),
),
),
);
},
separatorBuilder: (BuildContext context, int index) => Divider(),
))
],
),
),
);
}
#override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}

flutter clean will rebuild the /build folder (Flutter's Build Cache). It's a safe command to run, but I don't think it has anything to do with your problem.
Seems like you're removing and item at some index from your List produits them trying to access it latter in the code.

Related

How to manually add items to listview in Flutter

I have a list of cart items which I am displaying using the code below. Right after this list, I would like to display the total amount. This is how the final result should look like:
Chicken Burger 1X $20.5
Chicken Wrap 1X $9.99
Total $30.49
Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 4),
height: min(widget.order.products.length * 20.0 + 10, 100),
child: ListView(
children: widget.order.products
.map(
(prod) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(child:Text(
prod.title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
)),
Text(
'${prod.quantity}x \$. ${prod.price}',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
)
],
),
)
.toList(),
How can I append total to this list?
Here is my suggestion.
I used spread operator to ListView's children for adding Widget related to 'total'.
Additionally I added one item at Container's height because of Total item in ListView.
Below is summary code that I did.
ListView(
children: <Widget> [
...list.map(...).toList(),
TotalWidget(),
]
)
This is full code based your code.
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
backgroundColor: Colors.blueGrey,
isScrollControlled: false,
builder: (context) => Wrap(
children: [
ListView.separated(
shrinkWrap: true,
itemCount: 3,
itemBuilder: (BuildContext context, int index) => ListTile(
title: Text(
'lists[index].listName',
style: TextStyle(
color: Colors.white,
),
),
),
separatorBuilder: (BuildContext context, int index) =>
Divider(),
),
],
),
);
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
Widget _buildBody() {
List<Product> listProduct = [
Product('Chicken Burger', 1, 20.5),
Product('Chicken Wrap', 1, 9.99),
];
double totalAmount = 0;
for (var item in listProduct) {
totalAmount += (item.price * item.quantity);
}
return Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 4),
height: min((listProduct.length + 1) * 20.0 + 10, 100),
child: ListView(
children: [
...listProduct
.map(
(prod) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Text(
prod.title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
)),
Text(
'${prod.quantity}x \$. ${prod.price}',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
)
],
),
)
.toList(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Text(
'Total',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
)),
Text(
'$totalAmount',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
)
])
],
),
);
}
}
class Product {
String title;
int quantity;
double price;
Product(this.title, this.quantity, this.price);
}
Edit 1, after op updated more info in comments:
Column(children: [ Text(widget.order.totalPrice.toString()),
Flexible(child:
ListView(
children:
widget.order.products
.map((prod) => Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(child:Text(
prod.title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
)),
Text(
'${prod.quantity}x \$. ${prod.price}',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
)
],
),
)
.toList())]),
Since total isn't being stored as a single variable your list\cart object. You need to create a double totalPrice = 0.0;
then use a forLoop to add the values
for (var prod in widget.order.products) {
totalPrice += (prod.price * prod.quantity);}
Display this totalPrice wherever you want, you can't have it in the listView though.
If you want add items to ListView, first you have to add those items to your List (for example order.products or new one) and then use state management approach to re render ListView. if your logic is simple you can use stateful widget.
example code:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyList(),
),
);
}
}
class MyList extends StatefulWidget {
#override
_MyListState createState() => _MyListState();
}
class _MyListState extends State<MyList> {
List<String> orders = ["order1", "order2", "order3"];
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: ListView(
children: orders
.map(
(String e) => Card(
child: ListTile(
title: Text(e),
),
),
)
.toList(),
),
),
TextButton(
onPressed: () {
List<String> extraFields = ["field1", "field2"];
setState(
() {
orders.addAll(extraFields);
},
);
},
child: Text("Add extra fields"),
),
],
);
}
}
For that, you'd have to learn state management. To make things simple we'll use the built-in StreamBuilder to provide the data. Its best practice to separate your ui from your business logic so I'll do it here.
In order to use a StreamBuilder, you'd have to provide it a Stream<T> where T is your variable's type. In your case, its a List<String>. Lets write it in another file that holds all your buisness logic.
product_bloc.dart:
class ProductBloc {
final List<String> _productList = ["Item One", "Item Two"];
StreamController<List<String>> _products = StreamController<List<String>>();
Stream<List<String>> get products => _products.stream;
ProductBloc() {
_products.add(_productList);
}
void addProductAfterDelay() async {
_productList.add("Item Three");
await Future.delayed(const Duration(seconds: 3));
_products.add(_productList);
}
}
product_screen.dart:
StreamBuilder<List<String>>(
initialData: [],
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Text(snapshot.data[index]);
});
},
);

How to add Progress Indicator on Cards while tap in Flutter?

I am using Cards in Flutter and want Progress Indicator at the left bottom position for 2 seconds while Tap on the card so that another page load successfully.
Does anyone know how to add?
Container(
height: 130,
child: Card(
child: Row(
children: <Widget>[
Expanded(
child: ListTile(
title: Text(
'My card Location',
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.w700),
),
leading: Icon(Icons.setting),
// color: Colors.blueAccent, size: mediumIconSize),
trailing: Icon(Icons.keyboard_arrow_right),
selected: true,
onTap: () async {
// I try this one but not working
// Flushbar(
//
// showProgressIndicator: true,
// duration: Duration(seconds: 2),
// );
getDetails().then((myCardlocations) {
Navigator
.of(context)
.pushNamed('/myCardlocations',
arguments: ObjectLocations(locations, 'myCardlocations'));
}
);
}
),
),
],
),
),
),
You can do something like this using Stack and CircularProgressIndicator..
class _MyWidgetState extends State<MyWidget> {
bool isLoading = false;
#override
Widget build(BuildContext context) {
return Container(
height: 130,
child: Stack(
children: [
Container(
height: 130,
child: Card(
child: Row(
children: <Widget>[
Expanded(
child: ListTile(
title: Text(
'My card Location',
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.w700),
),
leading: Icon(Icons.settings),
// color: Colors.blueAccent, size: mediumIconSize),
trailing: Icon(Icons.keyboard_arrow_right),
selected: true,
onTap: () async {
setState(() {
isLoading = true;
});
getDetails().then((myCardLocations) {
setState(() {
isLoading = false;
});
// navigation code here
});
},
),
),
],
),
),
),
Align(
alignment: Alignment.bottomLeft,
child: isLoading
? Padding(
padding: EdgeInsets.fromLTRB(15,0,0,15),
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(),
),
)
: SizedBox(),
),
],
),
);
}
}
Edit:
Looks like I misunderstood the question a bit. Specifically, the place where to show the progress indicator. Anyways, if you get the idea, you can put the indicator at a different place as per your requirement.
There are certain things, which I would like to mention before I give the actual answer.
Read about Flutter.delayed constructor, very useful thing to make some thing wait for a while and do the operation by providing Duration. Whatever you want to do after that duration, it will implement in the callback function
Future.delayed(Duration(seconds: your_time, (){
//it will perform this operation after that much of seconds
}));
You can always show/hide a Widget using bool value, and make changes accordingly
Use a column and Add the LinearProgressIndicator at the end of the Widget. Show/hide it based up on the data
Also, use MediaQuery to give out the height. It is more efficient way of giving the dimensions according to all phone size. Like match-parent in Android Studio. Do the math accordingly, I have shown in the code also
Column(
children: [
Row(),
bool val ? LinearProgressIndicator() : Container() // Container() is nothing but an empty widget which shows nothing
]
)
Some heads up: I have not used getData, since it is not defined properly but you can call it the in function which I will show you in the code, that is pageTransit(). Follow the comments and you are good to go
class _MyHomePageState extends State<MyHomePage> {
// this takes care of the show/hide of your progress indicator
bool _showProgress = false;
// this takes care of the operation
void pageTransit(){
// first show when the ListTile is clicked
setState(() => _showProgress = true);
Future.delayed(Duration(seconds: 2), (){
// hide it after 2 seconds
setState(() => _showProgress = false);
// do the page trnasition here
//getDetails().then((myCardlocations) {
//Navigator.of(context).pushNamed('/myCardlocations',
//arguments: ObjectLocations(locations, 'myCardlocations'));
//}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: MediaQuery.of(context).size.height * 0.1,
child: Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// use your items here, based upon the bool value show hide your
// progress indicator
Row(
children: <Widget>[
Expanded(
child: ListTile(
title: Text(
'My card Location',
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.w700),
),
leading: Icon(Icons.settings),
// color: Colors.blueAccent, size: mediumIconSize),
trailing: Icon(Icons.keyboard_arrow_right),
selected: true,
onTap: () => pageTransit()
)
)
]
),
// show/hide in the card
_showProgress ? LinearProgressIndicator() : Container()
]
)
)
)
);
}
}
Result
Look at the ProgressIndicator, it remains there for 2 seconds, and then goes away
1. You need to define a GlobalKey for the Scaffold so that you can use a SnackBar (you can define the GloablKey in your page's State).
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
2. You need to set the key for the Scaffold.
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
...
3. You need to wrap the Card with a GestureDetector and set the onTap function to call showLoading which shows a SnackBar on the bottom of the screen. Call your getDetails function in the showLoading. Full code (except the define key step):
void _showLoading() {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
duration: new Duration(seconds: 2),
content: new Row(
children: <Widget>[
new CircularProgressIndicator(),
new Text("Loading...")
],
),
));
// call to your getDetails and its steps should be here
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("My app"),
),
body: Center(
child: GestureDetector(
child: Card(
child: Row(children: <Widget>[
Expanded(
child: ListTile(
title: Text(
'My card Location',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w700),
),
leading: Icon(Icons.settings),
// color: Colors.blueAccent, size: mediumIconSize),
trailing: Icon(Icons.keyboard_arrow_right),
selected: true,
)),
])),
onTap: () => _showLoading(),
)),
);
}
}
Note: you can also style the SnackBar.
Result:

Persistent bottom navigation bar flutter

I used a bottom navigation bar in flutter using this widget,
how can I make that bottom navigation bar show on all the pages?
and can I make it appear when I choose a page from drawer??
please help me,
You can actually achieve this with the pageview widget
https://api.flutter.dev/flutter/widgets/PageView-class.html
With this, you can have all the pages inside one class and build the bottom navigation bar underneath the pageview widget. By default the pages are swipeable but you can disable it doing
Scaffold(
body:
Container(
child:
Column(
children: <Widget> [
PageView(
physics:new NeverScrollableScrollPhysics())
controller: _controller,
children: [
MyPage1(),
MyPage2(),
MyPage3(),
],
),
googleNavBar()
]
)
);
May I suggest you to use flutter builtin BottomNavigationBar widget instead of third party widget.
Here is my code you can modify as per you requirement. Hope this will help.
class DashboardScreen extends StatefulWidget {
#override
_DashboardScreenState createState() => _DashboardScreenState();
}
class _DashboardScreenState extends State<DashboardScreen> with SingleTickerProviderStateMixin {
final _selectedItemColor = Colors.white;
final _unselectedItemColor = Color(0xFF828282);
final _selectedBgColor = Color(0xFF00cde7);
final _unselectedBgColor = Colors.transparent;
int _currentIndex = 0;
GlobalKey<ScaffoldState> _key = GlobalKey();
// List of body of current screen you import/create from other dart file.
final List<Widget> _children = [
HomeScreen(),
AppointmentScreen(id: 1),
PaymentScreen(id: 1),
ProfileScreen(id: 1)
];
// List of dynamic app bar for different page. You can also import/create app bar easily
final List<Widget> _childAppBar = [
HomeAppBar(),
AppointmentAppBar(),
PaymentAppBar(),
ProfileAppBar()
];
void _onItemTapped(int index) {
setState(() {
_currentIndex = index;
});
debugPrint("Tapped item : $index");
}
Color _getBgColor(int index) =>
_currentIndex == index ? _selectedBgColor : _unselectedBgColor;
Color _getItemColor(int index) =>
_currentIndex == index ? _selectedItemColor : _unselectedItemColor;
Widget _buildIcon(IconData iconData, String text, int index) => Container(
width: MediaQuery.of(context).size.width,
height: kBottomNavigationBarHeight,
child: Material(
color: _getBgColor(index),
child: InkWell(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
child: Column(
children: [
Icon(iconData, color: _getItemColor(index)),
Text(text,
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500, fontFamily: 'Poppins', color: _getItemColor(index))),
],
),
),
],
),
onTap: () => _onItemTapped(index), // function responsible for navigation on tap
),
),
);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
key: _key,
appBar: _childAppBar.elementAt(_currentIndex), // this is dynamic app bar
body: _children.elementAt(_currentIndex), // this is dynamic body of the current screen
bottomNavigationBar:
BottomNavigationBar(
currentIndex: 0,
type: BottomNavigationBarType.fixed,
iconSize: 30.0,
items: [
BottomNavigationBarItem(
icon: _buildIcon(Icons.home, "Home", 0), // Check this _buildIcon function above
title: SizedBox.shrink(),
),
BottomNavigationBarItem(
icon: _buildIcon(Icons.group, "Appointment", 1),
title: SizedBox.shrink(),
),
BottomNavigationBarItem(
icon: _buildIcon(Icons.add_circle_outline, "Make Payment", 2),
title: SizedBox.shrink(),
),
BottomNavigationBarItem(
icon: _buildIcon( Icons.person_outline, "My Account", 3),
title: SizedBox.shrink(),
),
]
),
drawer: _currentIndex == 0 || _currentIndex == 3 ? Drawer( // check to show drawer on particular screen
child: ListView(
padding: const EdgeInsets.all(0.0),
children: <Widget>[
UserAccountsDrawerHeader(
accountName: Text("Mohammad Gayasuddin"),
accountEmail: Text("ladla8602#gmail.com"),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.white70,
)),
ListTile(
title: Text('Login'),
trailing: Icon(Icons.lock),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LoginScreen(),
),
);
}),
ListTile(
title: Text('Sign Up'),
trailing: Icon(Icons.add_circle_outline),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RegisterScreen(),
),
);
})
],
),
) : PreferredSize(
child: Container(),
preferredSize: Size(0.0, 0.0),
),
),
);
}
}

How to save downloaded data from an API to RAM in Flutter?

I'm writing a really simple app in Flutter, but I have a problem with state management.
Here's the video of what I have. Link: https://streamable.com/ir3ztr
The video shows my application, but when I switch a screen using Bottom Navigation Bar, the data loads again and again from the API. I don't want that. I want the once downloaded data to be saved in RAM and not being downloaded again from the API. Is that possible? I heard about Provider, but I don't know how to use that in my case.
Is there anyone who can help me?
My code:
World
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_placeholder_textlines/flutter_placeholder_textlines.dart';
import '../../models/world.dart';
import '../../data/world_service.dart';
class WorldScreenAndroid extends StatefulWidget {
#override
_WorldScreenAndroidState createState() => _WorldScreenAndroidState();
}
class _WorldScreenAndroidState extends State<WorldScreenAndroid> {
Future<World> futureWorld;
#override
void initState() {
super.initState();
futureWorld = fetchWorld();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8.0),
child: FutureBuilder<World> (
future: futureWorld,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView(
children: [
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('coronavirus_cases').tr(context: context),
subtitle: Text(NumberFormat('#,###,###', 'en_US').format(snapshot.data.cases).toString())
),
),
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('deaths').tr(context: context),
subtitle: Text(NumberFormat('#,###,###', 'en_US').format(snapshot.data.deaths).toString())
),
),
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('recovered').tr(context: context),
subtitle: Text(NumberFormat('#,###,###', 'en_US').format(snapshot.data.recovered).toString())
),
)
],
);
}
return ListView(
children: [
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('coronavirus_cases').tr(context: context),
subtitle: PlaceholderLines(
count: 1,
animate: true,
color: Colors.grey,
minWidth: 0.10,
maxWidth: 0.50,
),
),
),
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('deaths').tr(context: context),
subtitle: PlaceholderLines(
count: 1,
animate: true,
color: Colors.grey,
minWidth: 0.10,
maxWidth: 0.50,
),
),
),
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('recovered').tr(context: context),
subtitle: PlaceholderLines(
count: 1,
animate: true,
color: Colors.grey,
minWidth: 0.10,
maxWidth: 0.50,
),
),
)
],
);
},
)
);
}
}
Countries
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import '../../models/country.dart';
import '../../data/countries_service.dart';
class CountriesScreenAndroid extends StatefulWidget {
#override
_CountriesScreenAndroidState createState() => _CountriesScreenAndroidState();
}
class _CountriesScreenAndroidState extends State<CountriesScreenAndroid> {
Future<List<Country>> futureCountries;
#override
void initState() {
super.initState();
futureCountries = fetchCountries();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8.0),
child: FutureBuilder(
future: futureCountries,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: [
TextField(),
SizedBox(height: 10.0),
Expanded(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
final List<String> _countriesAllArgs = [
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].cases),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].todayCases),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].active),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].deaths),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].todayDeaths),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].recovered),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].critical)
];
return Card(
child: Padding(
padding: EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
snapshot.data[index].country,
style: TextStyle(
fontSize: 18.0,
color: Colors.grey[600],
),
),
SizedBox(height: 6.0),
Text(
'countries_all',
style: TextStyle(
fontSize: 14.0,
color: Colors.grey[800],
),
).tr(args: _countriesAllArgs),
],
),
)
);
}
),
)
],
);
}
return Center(
child: CircularProgressIndicator(),
);
},
)
);
}
}
Make sure to keep the loaded data in a parent widget, not in the widget your switching.
Have you considered using the BLOC framework?
In that case, you could nicely sepearte Business Logic from UI code and keep the loaded data in the bloc.
In detail have a blocProvider on top, wrapping your app.
https://bloclibrary.dev/#/flutterbloccoreconcepts?id=blocprovider
Alternatively, but be carefull with the handling though, you can just create an object, anywhere outside of any widget, and access it from anywhere.
class Data {
List<String> stringsLoadedFromWeb;
}
Data data = Data();
// from anywhere else, where you import the above file
data.stringsLoadedFromWeb = ...

How to create searchable ListView in popup flutter?

How it possible to create a listView with Search function in a popup flutter?
I call the listView using API laravel. I want data in the popup will be able to be select or user can search it in popup and then select it. As user select the data and click submit data will be able to post in database.
below is the function that I used to call the data
List _listViewData = List();
#override
initState() {
super.initState();
// when loading your widget for the first time, loads country from db
_countryA();
}
void _countryA() async {
// gets data from db
final countryA = await CallApi().getData('countries');
var resBody = json.decode(countryA.body);
setState(() {
// converts db row data to List<String>, updating state
_listViewData = resBody;
});
}
I just know how to call the data of country using dropdown in an alert button.
I dont want it to be display in dropdown but instead in a list in flutter.
below is function that I called in the dropdown
void _showDialog() {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Select Country", textAlign:TextAlign.center,),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
content: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: DropdownButton(
items: _listViewData.map((item) {
return new DropdownMenuItem(
child: new Text(item['country']),
value: item['id'].toString(),
);
}).toList(),
onChanged: (newVal) {
setState(() {
_mySelectionAr = newVal;
});
},
value: _mySelectionAr,
),
),
],
),
);
},
);
}
So, the conclusion is that I want it to be display in listview in the flutter popup not a dropdown in a popup. I just cannot figure out how to call all the list data of country in a list in popup include with the search function.
Edit
The most simple way is after folk this github.
You can update file https://github.com/figengungor/country_pickers/blob/master/lib/countries.dart directly
or in
https://github.com/figengungor/country_pickers/blob/master/lib/country_picker_dialog.dart at line 113 change _allCountries to what you need, you can hard code or use your own ready made api but need to follow owner's Country Class.
You can use https://pub.dev/packages/country_pickers directly or reference source code and build your own
for Counties you need does not exist. you can fork this github project and modify directly https://github.com/figengungor/country_pickers/blob/master/lib/countries.dart
It support features you need can use in showDialog and inside is a ListView
also provide search by phone and name
full example code
import 'package:country_pickers/country.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:country_pickers/country_pickers.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Country Pickers Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/': (context) => DemoPage(),
},
);
}
}
class DemoPage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<DemoPage> {
Country _selectedDialogCountry =
CountryPickerUtils.getCountryByPhoneCode('90');
Country _selectedFilteredDialogCountry =
CountryPickerUtils.getCountryByPhoneCode('90');
Country _selectedCupertinoCountry =
CountryPickerUtils.getCountryByIsoCode('tr');
Country _selectedFilteredCupertinoCountry =
CountryPickerUtils.getCountryByIsoCode('DE');
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Country Pickers Demo'),
),
body: ListView(
padding: EdgeInsets.all(8.0),
children: <Widget>[
Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('CountryPickerDropdown'),
ListTile(title: _buildCountryPickerDropdown(false)),
],
),
),
Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('CountryPickerDropdown (filtered)'),
ListTile(title: _buildCountryPickerDropdown(true)),
],
),
),
Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('CountryPickerDialog'),
ListTile(
onTap: _openCountryPickerDialog,
title: _buildDialogItem(_selectedDialogCountry),
),
],
),
),
Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('CountryPickerDialog (filtered)'),
ListTile(
onTap: _openFilteredCountryPickerDialog,
title: _buildDialogItem(_selectedFilteredDialogCountry),
),
],
),
),
Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('CountryPickerCupertino'),
ListTile(
title: _buildCupertinoSelectedItem(_selectedCupertinoCountry),
onTap: _openCupertinoCountryPicker,
),
],
),
),
Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('CountryPickerCupertino (filtered)'),
ListTile(
title: _buildCupertinoSelectedItem(
_selectedFilteredCupertinoCountry),
onTap: _openFilteredCupertinoCountryPicker,
),
],
),
),
],
),
);
}
_buildCountryPickerDropdown(bool filtered) => Row(
children: <Widget>[
CountryPickerDropdown(
initialValue: 'AR',
itemBuilder: _buildDropdownItem,
itemFilter: filtered
? (c) => ['AR', 'DE', 'GB', 'CN'].contains(c.isoCode)
: null,
onValuePicked: (Country country) {
print("${country.name}");
},
),
SizedBox(
width: 8.0,
),
Expanded(
child: TextField(
decoration: InputDecoration(labelText: "Phone"),
),
)
],
);
Widget _buildDropdownItem(Country country) => Container(
child: Row(
children: <Widget>[
CountryPickerUtils.getDefaultFlagImage(country),
SizedBox(
width: 8.0,
),
Text("+${country.phoneCode}(${country.isoCode})"),
],
),
);
Widget _buildDialogItem(Country country) => Row(
children: <Widget>[
CountryPickerUtils.getDefaultFlagImage(country),
SizedBox(width: 8.0),
Text("+${country.phoneCode}"),
SizedBox(width: 8.0),
Flexible(child: Text(country.name))
],
);
void _openCountryPickerDialog() => showDialog(
context: context,
builder: (context) => Theme(
data: Theme.of(context).copyWith(primaryColor: Colors.pink),
child: CountryPickerDialog(
titlePadding: EdgeInsets.all(8.0),
searchCursorColor: Colors.pinkAccent,
searchInputDecoration: InputDecoration(hintText: 'Search...'),
isSearchable: true,
title: Text('Select your phone code'),
onValuePicked: (Country country) =>
setState(() => _selectedDialogCountry = country),
itemBuilder: _buildDialogItem)),
);
void _openFilteredCountryPickerDialog() => showDialog(
context: context,
builder: (context) => Theme(
data: Theme.of(context).copyWith(primaryColor: Colors.pink),
child: CountryPickerDialog(
titlePadding: EdgeInsets.all(8.0),
searchCursorColor: Colors.pinkAccent,
searchInputDecoration: InputDecoration(hintText: 'Search...'),
isSearchable: true,
title: Text('Select your phone code'),
onValuePicked: (Country country) =>
setState(() => _selectedFilteredDialogCountry = country),
itemFilter: (c) => ['AR', 'DE', 'GB', 'CN'].contains(c.isoCode),
itemBuilder: _buildDialogItem)),
);
void _openCupertinoCountryPicker() => showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) {
return CountryPickerCupertino(
backgroundColor: Colors.black,
itemBuilder: _buildCupertinoItem,
pickerSheetHeight: 300.0,
pickerItemHeight: 75,
initialCountry: _selectedCupertinoCountry,
onValuePicked: (Country country) =>
setState(() => _selectedCupertinoCountry = country),
);
});
void _openFilteredCupertinoCountryPicker() => showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) {
return CountryPickerCupertino(
backgroundColor: Colors.white,
pickerSheetHeight: 200.0,
initialCountry: _selectedFilteredCupertinoCountry,
onValuePicked: (Country country) =>
setState(() => _selectedFilteredCupertinoCountry = country),
itemFilter: (c) => ['AR', 'DE', 'GB', 'CN'].contains(c.isoCode),
);
});
Widget _buildCupertinoSelectedItem(Country country) {
return Row(
children: <Widget>[
CountryPickerUtils.getDefaultFlagImage(country),
SizedBox(width: 8.0),
Text("+${country.phoneCode}"),
SizedBox(width: 8.0),
Flexible(child: Text(country.name))
],
);
}
Widget _buildCupertinoItem(Country country) {
return DefaultTextStyle(
style:
const TextStyle(
color: CupertinoColors.white,
fontSize: 16.0,
),
child: Row(
children: <Widget>[
SizedBox(width: 8.0),
CountryPickerUtils.getDefaultFlagImage(country),
SizedBox(width: 8.0),
Text("+${country.phoneCode}"),
SizedBox(width: 8.0),
Flexible(child: Text(country.name))
],
),
);
}
}