In a grid view, since I have similar card items, I decided to create a custom widget containing each card item. The custom widget is a stateless widget. The problem I have is in passing an onTap property to the class. In fact, I do pass and no errors are there, but the onTap property does not propagate properly and it does not display the SnackBar I want. Here's the code:
import 'package:flutter/material.dart';
const _padding = EdgeInsets.all(8.0);
const _splashColor = Colors.amber;
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Main Page'),),
drawer: Drawer(
elevation: 8.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
DrawerHeader(
child: Column(
children: [
Image(image: AssetImage('assets/images/top_picture.png'), fit: BoxFit.scaleDown, width: 100, height: 100),
Text('Home', style: Theme.of(context).textTheme.headline6)
],
)),
ListTile(leading: Icon(Icons.settings), title: Text('Settings')),
ListTile(leading: Icon(Icons.exit_to_app), title: Text('Quit')),
AboutListTile(icon: Icon(Icons.info), aboutBoxChildren: [Text('Copyright (C) 2020'), Text('Design And Programming: me')],)
],
)),
body: HomeScreenBody(),
);
}
}
class HomeScreenBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding (
padding: const EdgeInsets.all(8.0),
child: GridView (
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
children: [
ItemCard(title: 'Balance', icon: 'assets/images/coins-balance.png', onTap: _comingSoon),
ItemCard(title: 'Add Funds', icon: 'assets/images/add-money.png', onTap: _comingSoon,),
ItemCard(title: 'Restaurant', icon: 'assets/images/restaurant.png', onTap: _comingSoon),
],
),
);
}
void _comingSoon(BuildContext context) {
print('Showing snackbar...');
final snack = SnackBar(content: Text('Coming soon...'));
Scaffold.of(context).showSnackBar(snack);
}
}
class ItemCard extends StatelessWidget{
final String icon;
final String title;
final ValueChanged<BuildContext> onTap;
const ItemCard({this.title, this.icon, this.onTap});
#override
Widget build(BuildContext context){
return Builder(builder:(context) {
return Card(
child: InkWell(
splashColor: _splashColor,
onTap: ()=> this.onTap,
child: Column (
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(image: Image.asset(this.icon).image, ),
Padding(
padding: _padding,
child: Text(this.title),
)
],
),
),
);
},
);
}
}
I tried changing the type of onTap field to ValueChanged, ValueChanged, ValueChanged<BuildContext), but none of them worked. Any idea why my callback is not called?
Try this
class ItemCard extends StatelessWidget{
final String icon;
final String title;
final void Function(BuildContext) onTap; //your function expects a context
const ItemCard({this.title, this.icon, this.onTap});
#override
Widget build(BuildContext context){
return Builder(builder:(ctx) { //changed to ctx so that contexts don't clash
return Card(
child: InkWell(
splashColor: _splashColor,
onTap: ()=> this.onTap(context), //pass context here
child: Column (
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(image: Image.asset(this.icon).image, ),
Padding(
padding: _padding,
child: Text(this.title),
)
],
),
),
);
},
);
}
}
Let me know if this doesn't work.. there is other simple way too.
Use a VoidCallback for your onTap variable.
Wrap the HomeScreen widget tree with a Builder widget so a context to be used in the SnackBar can be available.
I added a demo using your code as an example:
This works:
class ItemCard extends StatelessWidget{
final String icon;
final String title;
final VoidCallback onTap; // use a VoidCallback instead
const ItemCard({this.title, this.icon, this.onTap});
#override
Widget build(BuildContext context){
return Builder(builder:(context) {
return Card(
child: InkWell(
splashColor: _splashColor,
onTap: onTap, // assign the onTap property
child: Column (
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(image: Image.asset(this.icon).image, ),
Padding(
padding: _padding,
child: Text(this.title),
)
],
),
),
);
},
);
}
class HomeScreenBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Builder(
builder: (context){,
child: Padding (
padding: const EdgeInsets.all(8.0),
child: GridView (
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
children: [
ItemCard(title: 'Balance', icon: 'assets/images/coins-balance.png', onTap: () => _comingSoon(context)),
ItemCard(title: 'Add Funds', icon: 'assets/images/add-money.png', onTap: () => _comingSoon(context),),
ItemCard(title: 'Restaurant', icon: 'assets/images/restaurant.png', onTap: () => _comingSoon(context)),
],
),
);
},
);
}
void _comingSoon(context) {
print('Showing snackbar...');
final snack = SnackBar(content: Text('Coming soon...'));
Scaffold.of(context).showSnackBar(snack);
}
}
Change widget's onTap parameter to be a final Function(BuildContext) and then in onTap: change ()=> this.onTap to onTap(). That should do it.
Related
When I switch between pages in the drawer, the previous page appears for 1 second, then it closes and the page I want appears. https://youtube.com/shorts/YS5P2aQLBAM?feature=share
Every time this unwanted page appears, I get this error in the console
->E/Surface (25496): getSlotFromBufferLocked: unknown buffer: 0xf46fec90
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
children: [
//Drawer Header
//Drawer Body
Container(
padding: const EdgeInsets.only(left: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
drawerItem(context, () {
Get.to(() => ProfileScreen());
}, "Hesabım", Icons.person),
drawerItem(context, () {
Get.to(() => MyAdress());
}, "Adreslerim", Icons.location_on_sharp),
],
),
),
],
),
);
}
GestureDetector drawerItem(
BuildContext context, VoidCallback onTap, String? a, IconData icon) {
return GestureDetector(
onTap: onTap,
child: ListTile(
leading: Icon(icon, color: Colors.black54),
title: Text(
a.toString(),
style: TextStyle(color: Colors.black54),
),
),
);
}
}
You should extract into StatelessWidgets instead of extracting widget as a function.
I have a working example of the drawer with navigation in Flutter GetX. You can take a look at this:
class HomeView extends StatelessWidget {
const HomeView({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey.shade400,
drawer: Drawer(
child: ListView(
children: [
DrawerItem(
icon: Icons.abc,
onTap: () => Get.to(const DemoView()),
title: "Home",
),
],
),
),
body: const SizedBox.shrink(),
);
}
}
Here's the drawer item widget
class DrawerItem extends StatelessWidget {
final String title;
final VoidCallback onTap;
final IconData icon;
const DrawerItem({
Key? key,
required this.title,
required this.onTap,
required this.icon,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: ListTile(
leading: Icon(icon, color: Colors.black54),
title: Text(
title,
style: TextStyle(color: Colors.black54),
),
),
);
}
}
I'm trying to implement a bottomNavigationBar, but I can't finish it, I'm using Get to handle the routes and the state of the application.
I'm new to flutter, but reading the documentation I still don't understand
This is the main widget.
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: AppColors.black,
title: Center(
child: CommonAssetImage(
asset: 'logo.png',
color: AppColors.white,
height: 30,
),
),
),
body: BodyTabsScreen(),
bottomNavigationBar: HomeScreenBottomNavigatorBar()),
);
}
then,I have this widget where call other widget.In this widget I using Obs.
class HomeScreenBottomNavigatorBar extends StatelessWidget {
const HomeScreenBottomNavigatorBar({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(
elevation: 10,
color: AppColors.white,
child: Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 27),
color: AppColors.white,
child: Obx(() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TabsScreenBottomNavigationTab(
isActive: true,
label: 'Buy',
icon: Icons.home,
onTap: () {}),
TabsScreenBottomNavigationTab(
label: 'My account',
// icon: IkramIcons.user,
// iconSize: 20,
icon: (Icons.home),
onTap: () {}),
],
);
}),
),
);
}
}
class TabsScreenBottomNavigationTab extends StatelessWidget {
final String label;
final IconData icon;
final Widget image;
final VoidCallback onTap;
final bool isActive;
final double iconSize;
const TabsScreenBottomNavigationTab({
Key key,
this.label,
this.icon,
this.image,
this.onTap,
this.isActive,
this.iconSize = 20,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final _inactiveTextStyle = Theme.of(context).textTheme.bodyText2;
final _activeTextStyle =
_inactiveTextStyle.copyWith(color: AppColors.white);
const _commonDuration = Duration(milliseconds: 200);
final _availableSpace = MediaQuery.of(context).size.width - 27 * 2;
final _inactiveWidth = _availableSpace * .2;
final _activeWidth = _availableSpace * .35;
return AnimatedContainer(
duration: _commonDuration,
width: isActive ? _activeWidth : _inactiveWidth,
height: 35,
child: Material(
color: Colors.transparent,
shape: const StadiumBorder(),
clipBehavior: Clip.antiAlias,
child: AnimatedContainer(
duration: _commonDuration,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: AnimatedDefaultTextStyle(
style: isActive ? _activeTextStyle : _inactiveTextStyle,
duration: _commonDuration,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (icon != null)
Icon(
icon,
size: iconSize,
color: isActive ? AppColors.white : AppColors.black,
),
if (image != null) image,
if (isActive)
Container(
margin: const EdgeInsets.only(left: 8),
child: Text(label),
)
],
),
),
),
),
),
),
);
}
}
Getx will always throw that error when you use Obx or Getx widget without inserting an observable variable that widget. So if you are NOT trying to rebuild a widget based on an updated value of a variable that lives inside a class that exends GetxController, then don't use a Getx widget.
If you're just trying to use Getx for routing, then make sure to change your MaterialApp to GetMaterialApp and define your routes, like so.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
home: Page1(),
getPages: [
GetPage(name: Page1.id, page: () => Page1()), // add: static const id = 'your_page_name'; on each page to avoid using raw strings for routing
GetPage(name: Page2.id, page: () => Page2()),
],
);
}
}
Then in the onTap of your bottom navigation bar just use
Get.to(Page2());
Just remove the Obx widget wrapping your Row widget like this:
class HomeScreenBottomNavigatorBar extends StatelessWidget {
const HomeScreenBottomNavigatorBar({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(
elevation: 10,
color: AppColors.white,
child: Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 27),
color: AppColors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TabsScreenBottomNavigationTab(
isActive: true,
label: 'Buy',
icon: Icons.home,
onTap: () {}),
TabsScreenBottomNavigationTab(
label: 'My account',
// icon: IkramIcons.user,
// iconSize: 20,
icon: (Icons.home),
onTap: () {}),
],
);
),
);
}
}
Why? Because you are not using any observable (obs/Rx) variable in your widget tree which would trigger a rebuild when the value changes. So GetX is complaining and for good reason.
The controller should be inside Obx other wise its shows this error.
LeaderBoardController controller = Get.put(getIt<LeaderBoardController>());
Obx(()=>controller.leadBoardModel != null
? Column(
children: [
Container(
height: 180,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LeadBoardImage(type: LEADTYPE.NORMAL),
LeadBoardImage(type: LEADTYPE.CROWN),
LeadBoardImage(type: LEADTYPE.NORMAL)
]),
),
Expanded(
flex: 4,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: 10.w),
children: [
for (int i = 4; i < controller.leadBoardModel!.data.result.length; i++)
LeaderBoardListItem(result:controller.leadBoardModel!.data.result[i])
],
),
)
],
)
: LoadingContainer()),
It happens when you don't use your controller value in your widget. That's why it gives error because there is no sense in using Obx or Getx() widget
MainController controller = Get.find();
return Obx(
()
{
return Column(
children: [
Text("My pretty text")
],
);
}
);
Solution :
MainController controller = Get.find();
Obx(
()
{
return Column(
children: [
Text(controller.text)
],
);
}
);
Please note that there are two required aspects: 1) extending from a GetXController, and 2) The field/method returning a value from the controller, must be computed from a Rx type. In my case, I made a sub-class of a GetXController for a test, and the return value was hard-coded (not based on a Rx value), and the ObX error occurred.
For Current Scenario You dont need to use getx for this page
(Its not proper Implementation) . please remove the OBX() your error will gone .
class HomeScreenBottomNavigatorBar extends StatelessWidget {
const HomeScreenBottomNavigatorBar({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(
elevation: 10,
color: AppColors.white,
child: Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 27),
color: AppColors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TabsScreenBottomNavigationTab(
isActive: true,
label: 'Buy',
icon: Icons.home,
onTap: () {}),
TabsScreenBottomNavigationTab(
label: 'My account',
// icon: IkramIcons.user,
// iconSize: 20,
icon: (Icons.home),
onTap: () {}),
],
);
}),
),
}
I am trying to create a drawer..
I am working with customising every thing so I created a Custom Scaffold as the below code:
import 'package:flutter/material.dart';
import 'package:ipet/constants/constants.dart';
class IPetCustomScaffold extends StatelessWidget {
final Widget body;
final PreferredSizeWidget iPetTopAppBar;
final PreferredSizeWidget iPetBottomAppBar;
final Key ipKey;
final Color iPetBGScaffoldColor;
final Widget iPetDrawer;
const IPetCustomScaffold({
#required this.body,
this.ipKey,
#required this.iPetTopAppBar,
this.iPetBottomAppBar,
this.iPetBGScaffoldColor = AppConst.kPrimaryWhiteBgColor,
this.iPetDrawer,
});
#override
Scaffold build(BuildContext context) {
return Scaffold(
backgroundColor: iPetBGScaffoldColor,
key: ipKey,
appBar: iPetTopAppBar,
body: SafeArea(
child: body,
),
bottomNavigationBar: iPetBottomAppBar,
drawer: iPetDrawer,
);
}
}
and I have a custom AppBar as the below code:
import 'package:flutter/material.dart';
import 'package:ipet/constants/ipet_dimens.dart';
class IPetCustomTopBarWidget extends StatelessWidget
implements PreferredSizeWidget {
final String iPetPawImage;
final double iPetIconSize;
final IconData iPetListIconData;
final Widget iPetFirstPart;
final List<Widget> iPetMiddlePart;
final List<Widget> iPetLastPart;
#override
final Size preferredSize;
IPetCustomTopBarWidget({
this.iPetIconSize = IPetDimens.space20,
this.iPetListIconData,
this.iPetPawImage,
#required this.iPetFirstPart,
#required this.iPetMiddlePart,
this.iPetLastPart,
}) : preferredSize = Size.fromHeight(IPetDimens.space60);
ShapeBorder kBackButtonShape = RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(IPetDimens.space30),
),
);
#override
Widget build(BuildContext context) {
return SafeArea(
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(left: IPetDimens.space15),
child: iPetFirstPart,
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: iPetMiddlePart,
),
),
Row(
children: iPetLastPart,
),
],
),
);
}
}
Now I need to create a drawer in my DashBoard Page:
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return IPetCustomScaffold(
iPetTopAppBar: IPetCustomTopBarWidget(
iPetMiddlePart: [
DefaultImage(
image: 'assets/images/ipet_paw_img.png',
),
Label(
text: 'Settings',
),
],
iPetLastPart: [],
),
iPetDrawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough ver tical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
],
),
),
body: ...
);
the problem now the drawer does not working with this customised app bar works only with normal AppBar() as a default Widget and the result with the AppBar as the below image
and this is the result when using Custom App Bar
I may be working with wrong technique but I hope someone recommend a good advice :D
You can use this line wherever you want to open the drawer:
Scaffold.of(context).openDrawer();
Edit
when you are using a custom one, you have to assign a key to the Scaffold to differentiate which scaffold should open the Drawer
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
then after putting this Key in the Scaffold, call this whenver you want to open the drawer programmatically:
_scaffoldKey.currentState.openDrawer();
I need to set many items into a grid view. But I need to write something above the grid view and that's why I need a column which contains the texts and the grid view. But If I set some text and below the grid view then it doesn't work. Here is my code:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hamim Shop',
home: Scaffold(
body: Container(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text('Hamim Shop'),
GridView.count(
crossAxisCount: 3,
children: List.generate(choices.length, (index) {
return Center(
child: ChoiceCard(choice: choices[index]),
);
}),
),
],
),
),
),
);
}
}
Edited from Here:
Choice Card:
class ChoiceCard extends StatelessWidget {
const ChoiceCard({Key key, this.choice}) : super(key: key);
final Choice choice;
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: Icon(choice.icon, size: 150)),
Text(choice.title),
]),
));
}
}
...
class Choice {
const Choice({this.title, this.icon});
final String title;
final IconData icon;
}
const List<Choice> choices = const [
const Choice(title: 'Car', icon: Icons.directions_car),
.....
];
Use shrinkwrap:true & physics:NeverScrollableScrollPhysics()
physics:
Scroll physics that does not allow the user to scroll. Means only Column+SingleChildScrollView Scrolling work.
shrinkwrap:
If you do not set the shrinkWrap property, your GridView will be as big as its parent.
If you set it to true, the GridView will wrap its content and be as big as its children allow it to be.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hamim Shop',
home: Scaffold(
body: Container(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: Column(
children: [
Text('Hamim Shop'),
GridView.count(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
crossAxisCount: 3,
children: List.generate(choices.length, (index) {
return Center(
child: ChoiceCard(choice: choices[index]),
);
}),
),
],
),
),
),
),
);
}
}
class ChoiceCard extends StatelessWidget {
const ChoiceCard({Key key, this.choice}) : super(key: key);
final Choice choice;
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: Icon(choice.icon, size: 150)),
Text(choice.title),
]),
));
}
}
class Choice {
const Choice({this.title, this.icon});
final String title;
final IconData icon;
}
const List<Choice> choices = const [
const Choice(title: 'Car', icon: Icons.directions_car),
....
];
I am using the package
country_code_picker: ^1.4.0
https://pub.dev/packages/country_code_picker#-installing-tab-
with flutter 1.17.3
Which is pretty much one of the only country code picker packages. But I have one serious problem an I don't have a clue what it could be.
When I run this code
import 'package:flutter/material.dart';
import 'package:country_code_picker/country_code_picker.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
App();
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: TestWidget(),
);
}
}
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(body: _buildCountryPicker(context));
}
Widget _buildCountryPicker(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: CountryCodePicker(
initialSelection: 'NL',
),
),
);
}
}
And I open the dialog to select a country. I scroll in the list and then select the TextField my keyboard opens and when I try to type something my entire app freezes. I can't even hot reload. I don't get a single error.
I am running this on my Huawei P30, but I also experience this on other android devices. I don't know if this is a flutter bug or a country code picker bug.
I think it is probably in this widget somewhere. If anyone could point me in the right direction it would help me alot!
class SelectionDialog extends StatefulWidget {
final List<CountryCode> elements;
final bool showCountryOnly;
final InputDecoration searchDecoration;
final TextStyle searchStyle;
final TextStyle textStyle;
final WidgetBuilder emptySearchBuilder;
final bool showFlag;
final double flagWidth;
final Size size;
final bool hideSearch;
/// elements passed as favorite
final List<CountryCode> favoriteElements;
SelectionDialog(
this.elements,
this.favoriteElements, {
Key key,
this.showCountryOnly,
this.emptySearchBuilder,
InputDecoration searchDecoration = const InputDecoration(),
this.searchStyle,
this.textStyle,
this.showFlag,
this.flagWidth = 32,
this.size,
this.hideSearch = false,
}) : assert(searchDecoration != null, 'searchDecoration must not be null!'),
this.searchDecoration =
searchDecoration.copyWith(prefixIcon: Icon(Icons.search)),
super(key: key);
#override
State<StatefulWidget> createState() => _SelectionDialogState();
}
class _SelectionDialogState extends State<SelectionDialog> {
/// this is useful for filtering purpose
List<CountryCode> filteredElements;
#override
Widget build(BuildContext context) => SimpleDialog(
titlePadding: const EdgeInsets.all(0),
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
IconButton(
padding: const EdgeInsets.all(0),
iconSize: 20,
icon: Icon(
Icons.close,
),
onPressed: () => Navigator.pop(context),
),
if (!widget.hideSearch)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextField(
style: widget.searchStyle,
decoration: widget.searchDecoration,
onChanged: _filterElements,
),
),
],
),
children: [
Container(
width: widget.size?.width ?? MediaQuery.of(context).size.width,
height:
widget.size?.height ?? MediaQuery.of(context).size.height * 0.7,
child: ListView(
children: [
widget.favoriteElements.isEmpty
? const DecoratedBox(decoration: BoxDecoration())
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...widget.favoriteElements.map(
(f) => SimpleDialogOption(
child: _buildOption(f),
onPressed: () {
_selectItem(f);
},
),
),
const Divider(),
],
),
if (filteredElements.isEmpty)
_buildEmptySearchWidget(context)
else
...filteredElements.map(
(e) => SimpleDialogOption(
key: Key(e.toLongString()),
child: _buildOption(e),
onPressed: () {
_selectItem(e);
},
),
),
],
),
),
],
);
Widget _buildOption(CountryCode e) {
return Container(
width: 400,
child: Flex(
direction: Axis.horizontal,
children: <Widget>[
if (widget.showFlag)
Flexible(
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Image.asset(
e.flagUri,
package: 'country_code_picker',
width: widget.flagWidth,
),
),
),
Expanded(
flex: 4,
child: Text(
widget.showCountryOnly
? e.toCountryStringOnly()
: e.toLongString(),
overflow: TextOverflow.fade,
style: widget.textStyle,
),
),
],
),
);
}
Widget _buildEmptySearchWidget(BuildContext context) {
if (widget.emptySearchBuilder != null) {
return widget.emptySearchBuilder(context);
}
return Center(
child: Text('No country found'),
);
}
#override
void initState() {
filteredElements = widget.elements;
super.initState();
}
void _filterElements(String s) {
s = s.toUpperCase();
setState(() {
filteredElements = widget.elements
.where((e) =>
e.code.contains(s) ||
e.dialCode.contains(s) ||
e.name.toUpperCase().contains(s))
.toList();
});
}
void _selectItem(CountryCode e) {
Navigator.pop(context, e);
}
}
Also filed an issue on the flutter github https://github.com/flutter/flutter/issues/59886
Edit:
I have a video of it right here
https://www.youtube.com/watch?v=669KitFG9ek&feature=youtu.be
I just had to remove the keys, so there probably was a duplicate key
...filteredElements.map(
(e) => SimpleDialogOption(
//key: Key(e.toLongString()),
child: _buildOption(e),
onPressed: () {
_selectItem(e);
},
),
),