How to Customize DropDown in flutter? - flutter

In flutter i have tried to modify drop down like below
i have tried using overlay with container but overlay is taking positioned from entire page. how to give positioned from previous widget to overlay
```import 'package:flutter/material.dart';
List titles = ['a','b','c','d',];
class sample extends StatefulWidget {
#override
CountriesFieldState createState() => CountriesFieldState();
}
class CountriesFieldState extends State<sample> {
final FocusNode _focusNode = FocusNode();
OverlayEntry _overlayEntry;
final LayerLink _layerLink = LayerLink();
OverlayEntry _createOverlayEntry() {
RenderBox renderBox = context.findRenderObject();
var size = renderBox.size;
var offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (context) => Positioned(
child: Container(
height: 240,
width: 320,
child: Scaffold(
body: Container(
margin: EdgeInsets.only(top: 8),
//color: Colors.red,
child: Column(
children: <Widget>[
new Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Container(
//width: (320/360)*screenWidth,
decoration: new BoxDecoration(
border: new Border.all(color: Colors.white),
color: Colors.white),
child: new ListTile(
//dense: true,
contentPadding: EdgeInsets.only(
bottom: 0, left: 15, top: 0),
onTap: () {
_overlayEntry.remove();
},
title: new Text(
titles[index],
textAlign: TextAlign.left,
style: new TextStyle(
fontSize: 15,
fontFamily: "IBM Plex Sans Medium",
fontWeight: FontWeight.w500,
color: const Color(0xFF999aab)),
),
),
);
},
itemCount: titles.length,
)),
],
),
),
),
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
new InkWell(
child: Align(
alignment: Alignment.center,
child: Container(
margin: EdgeInsets.only(top: 100),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0, color: Colors.lightBlue.shade900),
),
// color: Colors.red,
),
height: 50.0,
width: 300.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[Text("abc")],
),
),
),
onTap: () {
this._overlayEntry = this._createOverlayEntry();
Overlay.of(context).insert(this._overlayEntry);
})
],
),
);
}
}```
i have used overlay concept to achieve this.
Is there any other solution to achieve customized dropdown???

This will help to achive your goal.
List titles = ['a','b','c','d',];
class Sample extends StatefulWidget {
#override
CountriesFieldState createState() => CountriesFieldState();
}
class CountriesFieldState extends State<Sample> {
final FocusNode _focusNode = FocusNode();
OverlayEntry _overlayEntry;
GlobalObjectKey _globalObjectKey = GlobalObjectKey(ValueKey('a_key_that_different_from_any_other')); // ADD THIS LINE
final LayerLink _layerLink = LayerLink();
OverlayEntry _createOverlayEntry() {
RenderBox renderBox =_globalObjectKey.currentContext?.findRenderObject(); //EDIT THIS LINE
var size = renderBox.size;
var offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (context) => Positioned(
child: Container(
height: 240,
width: 320,
child: Scaffold(
body: Container(
margin: EdgeInsets.only(top: 8),
//color: Colors.red,
child: Column(
children: <Widget>[
new Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Container(
//width: (320/360)*screenWidth,
decoration: new BoxDecoration(
border: new Border.all(color: Colors.white),
color: Colors.white),
child: new ListTile(
//dense: true,
contentPadding: EdgeInsets.only(
bottom: 0, left: 15, top: 0),
onTap: () {
_overlayEntry.remove();
},
title: new Text(
titles[index],
textAlign: TextAlign.left,
style: new TextStyle(
fontSize: 15,
fontFamily: "IBM Plex Sans Medium",
fontWeight: FontWeight.w500,
color: const Color(0xFF999aab)),
),
),
);
},
itemCount: titles.length,
)),
],
),
),
),
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
new InkWell(
child: Align(
alignment: Alignment.center,
child: Container(
key: _globalObjectKey, // ADD THIS LINE
margin: EdgeInsets.only(top: 100),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0, color: Colors.lightBlue.shade900),
),
// color: Colors.red,
),
height: 50.0,
width: 300.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[Text("abc")],
),
),
),
onTap: () {
this._overlayEntry = this._createOverlayEntry();
Overlay.of(context).insert(this._overlayEntry);
})
],
),
);
}
}

use DropdownButton Widget where you can pass any Custom widget as Item
Container(
width: 200,
child: DropdownButton(
isExpanded: true,
hint: Text('Select Working Time'),
value: selectedVal,
items: List.generate(
titles.length, (i) {
return DropdownMenuItem(
value:titles[i],
child: Text(
titles[i],
style: Theme.of(context)
.primaryTextTheme
.caption,
));
}),
onChanged: (c) {
selectedVal = c.toString().toLowerCase();
setState(() {});
}),
)

Related

How to make Flutter GridView change colors when tapped

I have asked this question previously but I am not receiving much help. I've created a GridView using GridView.count; however, I cannot get an indiviual container to change colors. The entire row changes if I click on any container within the row. I want to be able to change an individual container's color when it is tapped on, as well as have check mark appear on the top right corner of the container when selected.
(1) My Layout
(2) An Example of what I would like to happen
I'm very new to Flutter, so my code is not very optimal. I've tried making a list model as well but I have not had any luck with that. I'm attaching a portion of my code to show what I've done so far. Any help would be great :)
Widget build(BuildContext) {
double _height = MediaQuery.of(context).size.height;
final data = ModalRoute.of(context)!.settings;
String retrieveString;
if (data.arguments == null) {
retrieveString = "empty";
} else {
retrieveString = data.arguments as String;
}
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xff31708c),
body: Padding(
padding: EdgeInsets.only(
left: 30,
right: 30,
top: _height * 0.2),
child: Column(
children: <Widget>[
Text('Hi $retrieveString! What all would you like to focus on?',
style: GoogleFonts.montserrat(
color: Colors.white70,
fontSize: 19,
fontWeight: FontWeight.w600
),
textAlign: TextAlign.center,),
const SizedBox(height: 9),
Text("You can pick all that apply:",
style: GoogleFonts.montserrat(
color: Colors.white70,
fontSize: 14.5,
fontWeight: FontWeight.w600
),),
const SizedBox(height: 9,),
Column(children: [
GridView.count(
primary: true,
shrinkWrap: true,
padding: const EdgeInsets.all(10),
childAspectRatio: 1.15,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25,
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
_ContainerColor = _ContainerColor == Colors.white
? Color(0xffa1d0e6)
: Colors.white;
});
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
color: const Color.fromARGB(255, 20, 83, 106),
width: 2.5),
color: _ContainerColor
),
child: Column(
children: [
const Align(alignment: Alignment.topCenter,
child: Icon(MyFlutterApp.relationships,
color: Color(0xff31708c),
size: 45,),
),
const SizedBox(height: 4,),
Text('Maintaining healthy relationships',
style: GoogleFonts.montserrat(
fontSize: 14,
fontWeight: FontWeight.w500,
color: const Color(0xff31708c)
),
textAlign: TextAlign.center,)
],
),
),
),
From my understanding, you have to do allow users to have multi-select, because of the line
You can pick all that apply:
So here is a custom stateful widget that helps you to do multi-select, you can have your own widget child inside the gridview.
class CustomPage extends StatefulWidget {
const CustomPage({Key? key}) : super(key: key);
#override
State<CustomPage> createState() => _CustomPageState();
}
class _CustomPageState extends State<CustomPage> {
String retrieveString = "";
List selectedIndex = [];
List dataItems = ['India', 'USA', 'Germany'];
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
final data = ModalRoute.of(context)!.settings;
if (data.arguments == null) {
retrieveString = "empty";
} else {
retrieveString = data.arguments as String;
}
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xff31708c),
body: Padding(
padding: EdgeInsets.only(left: 30, right: 30, top: height * 0.2),
child: Column(children: <Widget>[
Text('Hi $retrieveString! What all would you like to focus on?'),
const SizedBox(height: 10),
const Text("You can pick all that apply:"),
const SizedBox(height: 10,),
Expanded(
child: GridView.builder(
scrollDirection: Axis.vertical,
primary: true,
shrinkWrap: true,
padding: const EdgeInsets.all(10),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 1.15,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25),
itemCount: dataItems.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
setState(() {
if (selectedIndex.contains(index)) {
selectedIndex.remove(index);
} else {
selectedIndex.add(index);
}
});
},
child: Stack(
alignment: Alignment.topRight,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
color:
const Color.fromARGB(255, 20, 83, 106),
width: 2.5),
color: selectedIndex.contains(index)
? const Color(0xffa1d0e6)
: Colors.white),
child: Center(
child: Text(dataItems[index]),
),
),
selectedIndex.contains(index)
? Container(
padding: const EdgeInsets.all(10),
child: const CircleAvatar(
child: Icon(Icons.check_outlined),
),
)
: Container()
],
),
);
},
),
)
])));
}
}
Hope it resolves your issue.
I've created your app as a test version. All you need to do is inject your widget in the //put your widget here!!!!! section. Also for testing this right now as a demo, you can paste the code on dartpad and select "New" -> "Flutter" -> Paste Code: https://dartpad.dev
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowtappedModeBanner: false,
home: Scaffold(
body: Center(
child: CustomCheckboxGrid(),
),
),
);
}
}
class CustomCheckboxGrid extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _CustomCheckboxGridState();
}
}
class _CustomCheckboxGridState extends State<CustomCheckboxGrid> {
int tapped_index = 0;
List card_names = [
'Maintaining healthy relationships',
'Being happier and more content in life',
'Work life balance',
'Personal Growth',
'Stress',
'Mental health',
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.builder(
padding: const EdgeInsets.all(16),
itemCount: card_names.length,
itemBuilder: (BuildContext context, int index) {
return buildCard(index);
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
),
);
}
Widget buildCard(int index) {
bool tapped = index == tapped_index;
String current_name = card_names[index];
return GestureDetector(
onTap: () {
setState(() {
print("Tapped index: ${index}");
tapped_index = index;
});
},
child: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(14),
child:
//put your widget here!!!!!
//-----------------------------------
Card(
color: tapped ? Colors.orange : Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Container(
child: Center(child: Text(current_name)),
),
),
//-----------------------------------
),
Positioned(
top: 14,
right: 14,
child: Offstage(
offstage: !tapped,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(width: 2),
shape: BoxShape.circle),
child: Icon(
Icons.check,
color: Colors.green,
),
),
),
),
],
),
);
}
}

Screen does not update even though build function is running flutter

So I have created a chat app which draws from a pusher client. Whenever there is a new message, the build function does rebuild, and I believe the widget list does change, but there is no update on the screen. How do I fix this ?
Widget build(BuildContext context) {
// print(messageWidgetList.length);
return Scaffold(
backgroundColor: AppColors.lightGrey,
appBar: AppBar(
backgroundColor: Colors.transparent,
title: Text(
messageTo,
style: TextStyle(
color: AppColors.white,
fontSize: 22,
),
),
),
body: Stack(
children: [
Padding(
padding:
const EdgeInsets.only(top: 12, left: 12, right: 12, bottom: 70),
child: ValueListenableBuilder<List<Widget>>(
valueListenable: messageWidgetList,
builder: (context, value, widget) {
print("Updated");
print(value.length);
// print(widget);
return ListView.builder(
// controller: scrollController,
physics: AlwaysScrollableScrollPhysics(),
reverse: true,
addAutomaticKeepAlives: true,
itemCount: value.length,
itemBuilder: (ctx, index) {
// print(index);
return value[index];
},
);
},
),
),
Align(
alignment: Alignment.bottomCenter,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (xFilesImages.isNotEmpty)
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: xFilesImages.map<Widget>((element) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: SizedBox(
height: 100,
width: 80,
child: Image.file(
File(element.path),
frameBuilder:
(ctx, child, frame, wasSynchronouslyLoaded) {
return SizedBox(
width: MediaQuery.of(ctx).size.width,
height: MediaQuery.of(ctx).size.height,
child: Stack(
children: [
Align(
alignment: Alignment.topRight,
child: Container(
height: 25,
width: 25,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.lightestGrey,
),
child: FittedBox(
child: GestureDetector(
onTap: () {
xFilesImages.remove(element);
setState(() {});
},
child:
const Icon(Icons.cancel)),
),
),
),
child
],
),
);
},
),
),
);
}).toList(),
),
),
const SizedBox(height: 5),
Container(
height: 60,
width: MediaQuery.of(context).size.width,
child: Padding(
padding:
const EdgeInsets.only(left: 10, bottom: 10, right: 10),
child: Container(
// height: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColors.darkGrey,
),
child: TextFormField(
// expands: true,
style: TextStyle(color: AppColors.white),
focusNode: messageFocusNode,
controller: messageController,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(
right: 8, left: 8, top: 14),
prefixIcon: InkWell(
onTap: () async {
if (!(await Permission.camera.isGranted)) {
await Permission.camera.request();
await Permission.photos.request();
}
ImagePicker _picker = ImagePicker();
xFilesImages =
await _picker.pickMultiImage() ?? [];
print("Got xFiles");
print(xFilesImages.length);
for (XFile xFile in xFilesImages) {
print(xFile.name);
print(xFile.path);
}
setState(() {});
},
child: Icon(
Icons.attachment,
size: 34,
color: AppColors.lightestGrey,
),
),
suffixIcon: GestureDetector(
onTap: () async {
//TODO: When you wake up, you have implemented picking images. Work on displaying picked images and then sending them
// loading = true;
// messageController.text = '';
if (messageController.text.isNotEmpty ||
xFilesImages.isNotEmpty) {
messageFocusNode.unfocus();
// messageWidgetList.add(sentMessage(
// {"message": messageController.text}));
setState(() {});
print("Sent button clicked");
ApiProvider.sendMessage(
widget.userModel.bearerToken,
widget.senderPhone.phoneNumbers.first,
messageTo,
messageController.text,
xFilesImages);
// loading = false;
messageController.text = '';
xFilesImages = [];
setState(() {});
}
},
child: const Icon(
Icons.send,
size: 30,
color: const Color(0xFF004b77),
),
),
fillColor: AppColors.lightGrey,
hintText: "Enter message...",
hintStyle:
TextStyle(color: AppColors.lightestGrey)),
),
),
),
),
],
),
),
if (loading)
Container(
height: double.infinity,
width: double.infinity,
color: AppColors.lightGrey.withOpacity(0.3),
child: Center(
child: SpinKitChasingDots(
color: AppColors.blue,
)),
)
],
),
);
}
Bad, does not work
static final List<Widget> items= [];
Widget build(BuildContext context) {
return ListView(children: items); // <-- look here
}
Good, does update properly
static final List<Widget> items= [];
Widget build(BuildContext context) {
return ListView(children: [...items]); // <-- look here
}
Grandious with the little extra mile
static final List<Widget> items= [];
Widget build(BuildContext context) {
return ListView(children: <Widget>[...items]); // <-- look here
}
setState needs a brand new object to update properly. It does not look into a List like here if something changed in there.

Calling a page using a provider inside a bottom navigation bar in flutter

Am using a provider for each page in my flutter application like so :
class HolidayListState extends State<HolidayListView>{
#override
Widget build(BuildContext context) {
final vm = Provider.of<HolidayListViewModel>(context);
if(vm.holidaysviews == null){
return Align(child: CircularProgressIndicator());
}else if(vm.holidaysviews.isEmpty) {
return Align(child: Text("No holidays found."));
}else{
return Scaffold(
backgroundColor: Color(0xfff0f0f0),
body:SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
Container(
padding: EdgeInsets.only(top: 145),
height: MediaQuery.of(context).size.height,
width: double.infinity,
child: ChangeNotifierProvider(
create: (_) => HolidayListViewModel(),
child: ListView.builder(
itemCount: vm.holidaysviews.length,
itemBuilder: (context, index) {
final holiday = vm.holidaysviews[index];
final item = holiday.toString();
return Dismissible(
key: UniqueKey(),
background: Container(
alignment: AlignmentDirectional.centerEnd,
color: Colors.red,
child: Icon(
Icons.delete,
color: Colors.white,
),
),
direction: DismissDirection.endToStart,
confirmDismiss:
(DismissDirection direction) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Confirm"),
content: const Text(
"Are you sure you wish to delete this item?"),
actions: <Widget>[
FlatButton(
onPressed: () async {
await HolidayWebServices().deleteHoliday(holiday.id.toString());
Navigator.of(context).pop(true);
},
child: const Text("DELETE")),
FlatButton(
onPressed: () =>
Navigator.of(context).pop(false),
child: const Text("CANCEL"),
),
],
);
},
);
},
onDismissed: (direction) {
if (!mounted) {
setState(() {
vm.holidaysviews.removeAt(index);
});
}
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.white,
),
width: double.infinity,
height: 110,
margin: EdgeInsets.symmetric(
vertical: 10, horizontal: 20),
padding: EdgeInsets.symmetric(
vertical: 10, horizontal: 20),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: 30,
height: 30,
margin: EdgeInsets.only(right: 15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
border: Border.all(
width: 3, color: Colors.deepPurple),
),
child: Text(
holiday.duration
.toString(),
textAlign: TextAlign.center,
),
),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
ConditionalBuilder(
condition:
holiday.status ==
"PENDING",
builder: (context) {
return Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(25),
color: Colors.white,
),
foregroundDecoration:
BadgeDecoration(
badgeColor: Colors.orange,
badgeSize: 70,
textSpan: TextSpan(
text:holiday.status,
style: TextStyle(
color: Colors.white,
fontSize: 12),
),
),
);
},
),
ConditionalBuilder(
condition:
holiday.status ==
"ACCEPTED",
builder: (context) {
return Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(25),
color: Colors.white,
),
foregroundDecoration:
BadgeDecoration(
badgeColor: Colors.green,
badgeSize: 70,
textSpan: TextSpan(
text: holiday.status,
style: TextStyle(
color: Colors.white,
fontSize: 12),
),
),
);
},
),
ConditionalBuilder(
condition:
holiday.status ==
"REFUSED",
builder: (context) {
return Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(25),
color: Colors.white,
),
foregroundDecoration:
BadgeDecoration(
badgeColor: Colors.red,
badgeSize: 70,
textSpan: TextSpan(
text: holiday.status,
style: TextStyle(
color: Colors.white,
fontSize: 12),
),
),
);
},
),
Row(
children: <Widget>[
Icon(
Icons.calendar_today,
color: Colors.deepPurple,
size: 20,
),
Text(
holiday.startDate,
style: TextStyle(
color: primary,
fontSize: 13,
letterSpacing: .3)),
],
),
SizedBox(
height: 6,
),
Row(
children: <Widget>[
Icon(
Icons.calendar_today,
color: Colors.deepPurple,
size: 20,
),
Text(holiday.endDate,
style: TextStyle(
color: primary,
fontSize: 13,
letterSpacing: .3)),
],
),
SizedBox(
height: 6,
),
Row(
children: <Widget>[
Icon(
Icons.assignment,
color: Colors.deepPurple,
size: 20,
),
SizedBox(
width: 5,
),
Text(holiday.type,
style: TextStyle(
color: primary,
fontSize: 13,
letterSpacing: .3)),
],
),
],
),
)
],
),
),
);
},
),
),
),
Container(
height: 140,
width: double.infinity,
decoration: BoxDecoration(
color: primary,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Holidays",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 24),
),
],
),
),
),
Container(
child: Column(
children: <Widget>[
SizedBox(
height: 110,
),
],
),
)
],
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
backgroundColor: kPrimaryColor,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return StepperDemo();
},
),
);
},
)
);
}}
}
class BadgeDecoration extends Decoration {
final Color badgeColor;
final double badgeSize;
final TextSpan textSpan;
const BadgeDecoration(
{#required this.badgeColor,
#required this.badgeSize,
#required this.textSpan});
#override
BoxPainter createBoxPainter([onChanged]) =>
_BadgePainter(badgeColor, badgeSize, textSpan);
}
class _BadgePainter extends BoxPainter {
static const double BASELINE_SHIFT = 1;
static const double CORNER_RADIUS = 4;
final Color badgeColor;
final double badgeSize;
final TextSpan textSpan;
_BadgePainter(this.badgeColor, this.badgeSize, this.textSpan);
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
canvas.save();
canvas.translate(
offset.dx + configuration.size.width - badgeSize, offset.dy);
canvas.drawPath(buildBadgePath(), getBadgePaint());
// draw text
final hyp = math.sqrt(badgeSize * badgeSize + badgeSize * badgeSize);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
textAlign: TextAlign.center);
textPainter.layout(minWidth: hyp, maxWidth: hyp);
final halfHeight = textPainter.size.height / 2;
final v = math.sqrt(halfHeight * halfHeight + halfHeight * halfHeight) +
BASELINE_SHIFT;
canvas.translate(v, -v);
canvas.rotate(0.785398); // 45 degrees
textPainter.paint(canvas, Offset.zero);
canvas.restore();
}
Paint getBadgePaint() => Paint()
..isAntiAlias = true
..color = badgeColor;
Path buildBadgePath() => Path.combine(
PathOperation.difference,
Path()
..addRRect(RRect.fromLTRBAndCorners(0, 0, badgeSize, badgeSize,
topRight: Radius.circular(CORNER_RADIUS))),
Path()
..lineTo(0, badgeSize)
..lineTo(badgeSize, badgeSize)
..close());
}
and I want to call the page in a navigation bar , right now this is the navigation bar page :
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:helium/views/holiday/holidayList.dart';
import 'package:helium/views/login/login_screen.dart';
import 'package:helium/views/profile/profile_home.dart';
import 'package:helium/views/time_tracking/time_tracking_home.dart';
class MainMenu extends StatefulWidget {
#override
_MainMenuState createState() => _MainMenuState();
void signOut() {}
}
class _MainMenuState extends State<MainMenu> {
PageController _pageController;
int _page = 0;
List icons = [
Icons.home,
Icons.event,
Icons.beach_access_rounded
// Icons.ac_unit,
];
List<Widget> _widgetOptions = <Widget>[
Profile(),
MyCalendarPage(),
HolidayListView()
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).primaryColor,
actions: <Widget>[
IconButton(
onPressed: () {
signOut();
},
icon: Icon(Icons.lock_open),
)
],
),
body: PageView(
physics: NeverScrollableScrollPhysics(),
controller: _pageController,
onPageChanged: onPageChanged,
children: _widgetOptions,
),
bottomNavigationBar: bottomMenu(),
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
Widget bottomMenu(){
return BottomAppBar(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
SizedBox(width: 2),
buildTabIcon(0),
buildTabIcon(1),
buildTabIcon(2),
SizedBox(width: 2),
],
),
color: Theme.of(context).primaryColor,
shape: CircularNotchedRectangle(),
);
}
void navigationTapped(int page) {
_pageController.jumpToPage(page);
}
signOut() {
setState(() {
widget.signOut();
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
);
print("signed out");
});
}
#override
void initState() {
super.initState();
// getPref();
_pageController = PageController();
}
#override
void dispose() {
super.dispose();
_pageController.dispose();
}
void onPageChanged(int page) {
setState(() {
this._page = page;
});
}
buildTabIcon(int index) {
return IconButton(
icon: Icon(
icons[index],
size: 24.0,
),
color: Colors.grey,
//_page == index
//? Theme.of(context).accentColor
//: Theme.of(context).textTheme.caption.color,
onPressed: () => _pageController.jumpToPage(index),
);
}
}
apparently I have to call the provider somewhere in the bottom nav bar page but I don't know how to do it , right now am getting this error , so if anyone knows what seems to be wrong , I would appreciate so me help , here's the stack trace :
Error: Could not find the correct Provider<HolidayListViewModel> above this HolidayListView Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that HolidayListView is under your MultiProvider/Provider<HolidayListViewModel>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
consider using `builder` like so:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}

I try to use Lifting State Up in Flutter, but I get "Closure call with mismatched arguments"

I pass Data from parent to child, and one of these parameters is a Fn. What I need is to return data from child to parent again, but I get an error as below:
[ Closure call with mismatched arguments: function '_ReservationBranchesSlotsScreenState._changeData' Receiver: Closure:
({String areaId, int coastPerPerson, int selectedBranchChecked, String
formatted, bool showCreateReservationButton, bool isExpanded, int
expandedIndex}) => void from Function '_changeData#176179245':. Tried
calling: _ReservationBranchesSlotsScreenState._changeData(areaId:
"d98a4e0e-d408-40c8-b387-9a405683a389", coastPerPerson: 0,
expandedIndex: -1, formatted: null, isExpanded: false,
selectedBrnachChecked: 0, showCreateReservationButton: false) Found:
_ReservationBranchesSlotsScreenState._changeData({String areaId, int coastPerPerson, int selectedBranchChecked, String formatted, bool
showCreateReservationButton, bool isExpanded, int expandedIndex}) =>
void ]
I create a Function in the parent Widget that do some actions, and pass this Fn to the child widget as below.
This is the parent widget Fn:
void _changeData({
String areaId,
int coastPerPerson,
int selectedBranchChecked,
String formatted,
bool showCreateReservationButton,
bool isExpanded,
int expandedIndex
}){
setState(() {
_areaId = areaId;
_coastPerPerson = coastPerPerson;
_selectedBranchChecked = selectedBranchChecked;
_formatted = formatted;
_showCreateReservationButton = showCreateReservationButton;
_isExpanded = isExpanded;
_expandedIndex = expandedIndex;
});
}
also below when I pass this Fn to the child widget:
SelectBranchWidget(
branches: _branches,
coastPerPerson: _coastPerPerson,
areaId: _areaId,
selectedBranchChecked: _selectedBranchChecked,
formatted: _formatted,
isExpanded: _isExpanded,
showCreateReservationButton: _showCreateReservationButton,
expandedIndex: _expandedIndex,
***changeData: _changeData,***
),
and here is the child widget which I need to return a Data from it to the parent again:
class SelectBranchWidget extends StatefulWidget {
List<RestaurantBranch> branches;
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
SelectBranchWidget(
{this.branches,
this.coastPerPerson,
this.areaId,
this.selectedBranchChecked,
this.formatted,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
this.changeData,
});
#override
_SelectBranchWidgetState createState() => _SelectBranchWidgetState();
}
class _SelectBranchWidgetState extends State<SelectBranchWidget> {
void _changedValues(int i, int branchAreaIndex) {
widget.coastPerPerson = widget.branches[i].branchAreas[branchAreaIndex].costPerSeat;
widget.areaId = widget.branches[i].branchAreas[branchAreaIndex].guid;
print('areaId IS ${widget.areaId}');
widget.selectedBranchChecked = i;
widget.formatted = null;
widget.isExpanded = false;
widget.showCreateReservationButton = false;
widget.expandedIndex = -1;
widget.changeData(
areaId: widget.areaId,
coastPerPerson: widget.coastPerPerson,
selectedBrnachChecked:widget.selectedBranchChecked,
formatted: widget.formatted,
showCreateReservationButton:widget.showCreateReservationButton,
isExpanded:widget.isExpanded,
expandedIndex: widget.expandedIndex
);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(Radius.circular(10))),
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Select Branch',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 8,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
'Number of branches = ${widget.branches.length}',
style: TextStyle(color: Colors.grey, fontSize: 10),
),
),
),
SizedBox(
height: 16,
),
ConstrainedBox(
constraints:
BoxConstraints(maxHeight: 230, maxWidth: double.infinity),
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.branches.length,
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () {
if (widget.branches[i].branchAreas.length == 1) {
_changedValues(i, 0);
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).backgroundColor,
title: Text(
'Select Area',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
content: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
// height: (_branches[i].branchAreas.length == 1)
// ?70
// : (_branches[i].branchAreas.length == 2)
// ? 100
// :150,
width: 100.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 120),
child: ListView.builder(
shrinkWrap: true,
itemCount:
widget.branches[i].branchAreas.length,
itemBuilder:
(BuildContext context, int index) {
return GestureDetector(
onTap: () {
_changedValues(i, index);
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.only(
top: 16, left: 36.0, right: 36),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.branches[i]
.branchAreas[index].name,
style: TextStyle(
color: Colors.white),
),
Divider(
color: Colors.grey,
thickness: 1,
),
],
),
),
);
},
),
),
),
);
});
},
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Theme.of(context).backgroundColor,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.branches[i].branchDistrict.name,
style: TextStyle(color: Colors.white, fontSize: 16),
),
(widget.selectedBranchChecked == i)
? Container(
height: 40,
width: 40,
child: Image.asset(
getAssetsName(AssetsImage.checkIcon),
fit: BoxFit.cover,
),
)
: Container(
padding: EdgeInsets.only(top: 5, bottom: 5),
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: SizedBox(),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
You can use two approaches to achieve this.
User first one if you want to return from a route (from next screen to previous one)
Use second Approach if you want to make changes to Parent Widget from its child widget on same screen.
1st Approach (Returning an object from route)
Create a Model of data you are passing.
class MyModel {
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
MyModel({
this.areaId,
this.coastPerPerson,
this.selectedBranchChecked,
this.formatted ,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
});
}
I'm assuming that You Parent Looks like this. And you're navigating from Parent Page to
SelectBranchWidget page
class YourParentWidget extends StatefulWidget {
YourParentWidget({Key key}) : super(key: key);
#override
_YourParentWidgetState createState() => _YourParentWidgetState();
}
class _YourParentWidgetState extends State<YourParentWidget> {
navigateAndChangeData(){
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return SelectBranchWidget();
})).then((value) {
///Use this to get value from next screen to PArent Screen
if(value != null) {
var model = value as MyModel;
//Now you have access to all returning values
//model.areaId
//model.coastPerPerson
//model.selectedBranchChecked
//...
///Make changes accordingly
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
///Your UI Conponents....
);
}
}
Now In SelectBranchWidget Widget, What you need to do in _changedValues function is to return model values. i.e.
class SelectBranchWidget extends StatefulWidget {
List<RestaurantBranch> branches;
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
SelectBranchWidget(
{this.branches,
this.coastPerPerson,
this.areaId,
this.selectedBranchChecked,
this.formatted,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
this.changeData,
});
#override
_SelectBranchWidgetState createState() => _SelectBranchWidgetState();
}
class _SelectBranchWidgetState extends State<SelectBranchWidget> {
void _changedValues(int i, int branchAreaIndex) {
MyModel model = MyModel(
areaId: widget.branches[i].branchAreas[branchAreaIndex].guid,
coastPerPerson: widget.branches[i].branchAreas[branchAreaIndex].costPerSeat,
selectedBranchChecked: 1,
formatted: null,
isExpanded: false,
showCreateReservationButton: false,
expandedIndex: -1
);
Navigator.of(context).pop(model);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(Radius.circular(10))),
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Select Branch',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 8,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
'Number of branches = ${widget.branches.length}',
style: TextStyle(color: Colors.grey, fontSize: 10),
),
),
),
SizedBox(
height: 16,
),
ConstrainedBox(
constraints:
BoxConstraints(maxHeight: 230, maxWidth: double.infinity),
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.branches.length,
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () {
if (widget.branches[i].branchAreas.length == 1) {
_changedValues(i, 0);
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).backgroundColor,
title: Text(
'Select Area',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
content: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
// height: (_branches[i].branchAreas.length == 1)
// ?70
// : (_branches[i].branchAreas.length == 2)
// ? 100
// :150,
width: 100.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 120),
child: ListView.builder(
shrinkWrap: true,
itemCount:
widget.branches[i].branchAreas.length,
itemBuilder:
(BuildContext context, int index) {
return GestureDetector(
onTap: () {
_changedValues(i, index);
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.only(
top: 16, left: 36.0, right: 36),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.branches[i]
.branchAreas[index].name,
style: TextStyle(
color: Colors.white),
),
Divider(
color: Colors.grey,
thickness: 1,
),
],
),
),
);
},
),
),
),
);
});
},
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Theme.of(context).backgroundColor,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.branches[i].branchDistrict.name,
style: TextStyle(color: Colors.white, fontSize: 16),
),
(widget.selectedBranchChecked == i)
? Container(
height: 40,
width: 40,
child: Image.asset(
getAssetsName(AssetsImage.checkIcon),
fit: BoxFit.cover,
),
)
: Container(
padding: EdgeInsets.only(top: 5, bottom: 5),
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: SizedBox(),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
2nd Approach (Making changes to Parent Widget while staying on child Widget)
Make your Parent Widget's State Class and changeData function public (i.e. remove underscore before the state class name)
i.e.
class YourParentWidget extends StatefulWidget {
YourParentWidget({Key key}) : super(key: key);
#override
YourParentWidgetState createState() => YourParentWidgetState();
}
class YourParentWidgetState extends State<YourParentWidget> {
String _areaId;
int _coastPerPerson;
int _selectedBranchChecked;
String _formatted;
bool _showCreateReservationButton;
bool _isExpanded;
int _expandedIndex;
void changeData({
String areaId,
int coastPerPerson,
int selectedBranchChecked,
String formatted,
bool showCreateReservationButton,
bool isExpanded,
int expandedIndex
}){
setState(() {
_areaId = areaId;
_coastPerPerson = coastPerPerson;
_selectedBranchChecked = selectedBranchChecked;
_formatted = formatted;
_showCreateReservationButton = showCreateReservationButton;
_isExpanded = isExpanded;
_expandedIndex = expandedIndex;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
///Your UI Conponents....
);
}
}
Then Pass widget.key fro YourParentWidget to yor SelectBranchWidget (If you'reusing it inside your UI of YourParentWidget) i.e.
return Scaffold(
body: Column(
children: [
///Your UI Conponents....
SelectBranchWidget(parentKey: widget.key,
///... Other PArameters as well
)
],
)
);
Now in Your SelectBranchWidget Widget, do the following
class SelectBranchWidget extends StatefulWidget {
GlobalKey<YourParentWidgetState> parentKey;
List<RestaurantBranch> branches;
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
SelectBranchWidget(
{
this.parentKey,
this.branches,
this.coastPerPerson,
this.areaId,
this.selectedBranchChecked,
this.formatted,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
this.changeData,
});
#override
_SelectBranchWidgetState createState() => _SelectBranchWidgetState();
}
class _SelectBranchWidgetState extends State<SelectBranchWidget> {
void _changedValues(int i, int branchAreaIndex) {
widget.coastPerPerson = widget.branches[i].branchAreas[branchAreaIndex].costPerSeat;
widget.areaId = widget.branches[i].branchAreas[branchAreaIndex].guid;
print('areaId IS ${widget.areaId}');
widget.selectedBranchChecked = i;
widget.formatted = null;
widget.isExpanded = false;
widget.showCreateReservationButton = false;
widget.expandedIndex = -1;
///Then do this to make changes to your parent widget's state
widget.parentKey.currentState.changeData(
areaId: widget.areaId,
coastPerPerson: widget.coastPerPerson,
selectedBrnachChecked:widget.selectedBranchChecked,
formatted: widget.formatted,
showCreateReservationButton:widget.showCreateReservationButton,
isExpanded:widget.isExpanded,
expandedIndex: widget.expandedIndex
);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(Radius.circular(10))),
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Select Branch',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 8,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
'Number of branches = ${widget.branches.length}',
style: TextStyle(color: Colors.grey, fontSize: 10),
),
),
),
SizedBox(
height: 16,
),
ConstrainedBox(
constraints:
BoxConstraints(maxHeight: 230, maxWidth: double.infinity),
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.branches.length,
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () {
if (widget.branches[i].branchAreas.length == 1) {
_changedValues(i, 0);
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).backgroundColor,
title: Text(
'Select Area',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
content: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
// height: (_branches[i].branchAreas.length == 1)
// ?70
// : (_branches[i].branchAreas.length == 2)
// ? 100
// :150,
width: 100.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 120),
child: ListView.builder(
shrinkWrap: true,
itemCount:
widget.branches[i].branchAreas.length,
itemBuilder:
(BuildContext context, int index) {
return GestureDetector(
onTap: () {
_changedValues(i, index);
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.only(
top: 16, left: 36.0, right: 36),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.branches[i]
.branchAreas[index].name,
style: TextStyle(
color: Colors.white),
),
Divider(
color: Colors.grey,
thickness: 1,
),
],
),
),
);
},
),
),
),
);
});
},
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Theme.of(context).backgroundColor,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.branches[i].branchDistrict.name,
style: TextStyle(color: Colors.white, fontSize: 16),
),
(widget.selectedBranchChecked == i)
? Container(
height: 40,
width: 40,
child: Image.asset(
getAssetsName(AssetsImage.checkIcon),
fit: BoxFit.cover,
),
)
: Container(
padding: EdgeInsets.only(top: 5, bottom: 5),
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: SizedBox(),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
Edit: You just need to call (wherever you're initiating it from) your YourParentWidget like YourParentWidget(key: GlobalKey<YourParentWidgetState>())
Pardon me if any typo occurs

CustomScrollView object does not update with input from StatefulBuilder

I have a CustomScrollView that gets updated upon user input. The actual items in the ListView are in a SliverChildBuilderDelegate inside the CustomScrollView which is inside the body of the Scaffold object (see the code below). If a user adds an item in the form that is inside the StatefulBuilder which is inside a showDialog object, the item does not get added to the planets list which thus does not update the ListView. I think the problem is caused by the StatefulBuilder which I need to update my DropdownButton.
My code:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() {
runApp(MaterialApp(
home: HomePage(),
));
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class Planet {
final String id;
final String name;
final String location;
final String distance;
final String gravity;
final String image;
const Planet({this.id, this.name, this.location, this.distance, this.gravity, this.image});
}
class Coin {
int id;
String name;
Coin(this.id, this.name);
static List<Coin> getCoins() {
return <Coin>[
Coin(1, 'coin1'),
Coin(2, 'coin2'),
Coin(3, 'coin3'),
Coin(4, 'coin4'),
Coin(5, 'coin5'),
];
}
}
class MenuItem {
String title;
String icon;
Color color;
Function func;
MenuItem(this.title, this.icon, this.color, this.func);
}
class _HomePageState extends State<HomePage> {
List<Coin> _coins = Coin.getCoins();
List<DropdownMenuItem<Coin>> _dropdownMenuItems;
Coin _selectedCoin;
#override
void initState() {
_dropdownMenuItems = buildDropdownMenuItems(_coins);
_selectedCoin = _dropdownMenuItems[0].value;
super.initState();
_menuItems = createMenuItems();
_selectedMenuItem = _menuItems.first;
}
MenuItem _selectedMenuItem;
List<MenuItem> _menuItems;
List<Widget> _menuOptionWidgets = [];
List<MenuItem> createMenuItems() {
final menuItems = [
new MenuItem("Dashboard", 'assets/images/dashboard.png', Colors.black, () => new Dashboard()),
new MenuItem("Cows", 'assets/images/cow.png', Colors.green, () => new Cows()),
];
return menuItems;
}
_onSelectItem(MenuItem menuItem) {
setState(() {
_selectedMenuItem = menuItem;
});
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
Navigator.of(context).pop(); // close side menu
}
List<DropdownMenuItem<Coin>> buildDropdownMenuItems(List coins) {
List<DropdownMenuItem<Coin>> items = List();
for (Coin coin in coins) {
items.add(
DropdownMenuItem(
value: coin,
child:
Text(
coin.name,
style: TextStyle(
fontSize: 18.0,
color: Colors.black87,
fontWeight: FontWeight.bold
),
),
),
);
}
return items;
}
onChangeDropdownItem(Coin selectedCoin, StateSetter setState) {
setState(() {
_selectedCoin = selectedCoin;
print('${_selectedCoin.name}');
});
}
final coinController = TextEditingController();
final amountController = TextEditingController();
final purposeController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
coinController.dispose();
amountController.dispose();
purposeController.dispose();
super.dispose();
}
List<Planet> planets = [];
#override
Widget build(BuildContext context) {
_menuOptionWidgets = [];
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd kk:mm').format(now);
for (var menuItem in _menuItems) {
_menuOptionWidgets.add(new Container(
decoration: new BoxDecoration(
color: menuItem == _selectedMenuItem
? Colors.grey[200]
: Colors.white),
child: new ListTile(
leading: new Image.asset(menuItem.icon),
onTap: () => _onSelectItem(menuItem),
title: Text(
menuItem.title,
style: new TextStyle(
fontSize: 20.0,
color: menuItem.color,
fontWeight: menuItem == _selectedMenuItem
? FontWeight.bold
: FontWeight.w300),
))));
_menuOptionWidgets.add(
new SizedBox(
child: new Center(
child: new Container(
margin: new EdgeInsetsDirectional.only(start: 20.0, end: 20.0),
height: 0.3,
color: Colors.grey,
),
),
),
);
}
double screenHeight;
screenHeight = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
title: Text('Dashboard'),
backgroundColor: Color.fromRGBO(53, 73, 94, 0.9),
elevation: 0.0,
// leading: Container(),
),
drawer: new Drawer(
child: new ListView(
children: <Widget>[
new Container(
child: new ListTile(
leading: new CircleAvatar(
backgroundColor: Colors.black,
radius: 40.0,
child: Text(
"L",style: TextStyle(
color: Colors.orange,
fontSize: 46.0),
),
),
title: Text("Welcome",style: TextStyle(fontSize: 46.0),)
),
margin: new EdgeInsetsDirectional.only(top: 20.0),
color: Colors.white,
constraints: BoxConstraints(maxHeight: 90.0, minHeight: 90.0)),
new SizedBox(
child: new Center(
child: new Container(
margin:
new EdgeInsetsDirectional.only(start: 10.0, end: 10.0),
height: 0.3,
color: Colors.black,
),
),
),
new Container(
color: Colors.white,
child: new Column(children: _menuOptionWidgets),
),
],
),
),
floatingActionButton: new Container(
width: 120.0,
height: 120.0,
padding: const EdgeInsets.only(bottom:40.0),
child: FloatingActionButton(
child: Icon(Icons.add,size: 50.0),
elevation: 0.0,
onPressed: () {
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Container(
margin: EdgeInsets.only(
top: screenHeight / 5,
bottom: screenHeight / 4
),
padding: EdgeInsets.only(left: 10, right: 10),
child: Card(
color: Color.fromRGBO(53, 73, 94, 0.9),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 8,
child: Padding(
padding: const EdgeInsets.all(30.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Text(
"Create",
style: Style.headerTextStyle
),
),
Divider(
color: Colors.white
),
SizedBox(
height: 15,
),
DropdownButton(
value: _selectedCoin,
items: _dropdownMenuItems,
onChanged: (selectedCoin) {
setState(() {
_selectedCoin = selectedCoin;
print('${_selectedCoin.name}');
});
}, //onChangeDropdownItem(_selectedCoin, setState),
),
SizedBox(
height: 15,
),
TextFormField(
decoration: InputDecoration(
labelText: "Amount",
hasFloatingPlaceholder: true,
labelStyle: Style.commonTextStyle
),
controller: amountController,
),
SizedBox(
height: 20,
),
TextFormField(
decoration: InputDecoration(
labelText: "What is it for?",
hasFloatingPlaceholder: true,
labelStyle: Style.commonTextStyle
),
controller: purposeController,
),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment
.spaceEvenly,
children: <Widget>[
Expanded(
child: Container(),
),
ButtonTheme(
minWidth: 150.0,
child: RaisedButton(
padding: EdgeInsets.all(8.0),
child: Text('Share',
style: TextStyle(
fontSize: 24,
color: Colors.black87,
fontWeight: FontWeight.bold
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
18.0)
),
color: Colors.white,
splashColor: Colors.blueGrey,
onPressed: () {
setState(() {
planets.add(Planet(
id: '1',
// TODO need to adjust this
name: purposeController.text,
location: '€' + amountController.text,
distance: formattedDate,
gravity: 'test',
image: _setImage(), // TODO might have to pass _selectedCoin as parameter
)
);
});
Navigator.pop(context);
},
),
),
],
),
],
),
),
),
);
}
);
},
);
},
),
),
body: Column(
children: <Widget>[
new Expanded(
child: new Container(
color: Color.fromRGBO(53, 73, 94, 0.9),
child: new CustomScrollView(
scrollDirection: Axis.vertical,
slivers: <Widget>[
new SliverPadding(
padding: const EdgeInsets.symmetric(vertical: 0.0),
sliver: new SliverFixedExtentList(
itemExtent: 152.0,
delegate: new SliverChildBuilderDelegate(
(context, index) => new PlanetRow(planets[index]),
childCount: planets.length,
),
),
),
],
),
),
),
],
),
);
}
}
The expected result is that the object from user input gets added to the planets list. The Sliver object then gets the updated planets list which shows the user input in a Planet Card. Any help will be greatly appreciated!