Calling a page using a provider inside a bottom navigation bar in flutter - 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>()),
}
),
}

Related

How to remove build side effects in this context, using Staggered Grid View Builder Flutter?

I am trying to create the build method without side effects (without blinking). I solved this problem using StatefulBuilder, but I read that I should rebuild 1000x times without any change or effect.
The Staggered Grid View Builder Widget are rebuilt when the keyboard is opening, or whenever I open again the page, or add/remove an item from it. That's good, normally, but with side effects like you see below. Maybe there is any solution to animate the remove/add functionality, or the infinite reloading and keep the rest of the items in cache. So I need to limit the builder recreate inside Grid View Builder?
On other applications I don't see this ugly "blinking". Where is the problem and how can I solve it? I used Animation Limiter but it's not working for me, neither PrecacheImage, somehow I need to rebuild without blink (first items).
My code:
class VisionBoard extends StatefulWidget {
const VisionBoard({Key? key}) : super(key: key);
#override
_VisionBoardState createState() => _VisionBoardState();
}
class _VisionBoardState extends State<VisionBoard> with SingleTickerProviderStateMixin {
ScreenshotController screenshotController = ScreenshotController();
String saveGoalsButtonText = "SAVE GOALS";
String wallpaperButtonText = "CREATE WALLPAPER";
String saveWallpaperButtonText = "SAVE";
bool createWallpaper = false;
bool isWallpaperCreated = false;
late File imageFile;
late String newImage;
late Uint8List imageRaw;
int noOfImages = 0;
late Uint8List wallpaperBytes;
String title = "My Vision Board";
String goals = "";
late List<String> visions = <String>[];
final TextEditingController _textFieldController = TextEditingController();
final TextEditingController _goalsController = TextEditingController();
static final _formKey = GlobalKey<FormState>();
#override
void initState() {
super.initState();
loadVisionBoardTitleAndImages();
}
void loadVisionBoardTitleAndImages() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
//await prefs.clear();
setState(() {
_textFieldController.text = prefs.getString('titlu') ?? title;
visions = prefs.getStringList('visions') ?? <String>[];
_goalsController.text = prefs.getString('goals') ?? goals;
});
}
void _removeVisions(int index) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
isButtonDisabled=true;
visions.removeAt(index);
prefs.setStringList('visions', visions);
createWallpaper = false;
wallpaperButtonText = "CREATE WALLPAPER";
isWallpaperCreated = false;
});
await CreateWallpaperLayouts().createWallpaper(visions).then((value) {
setState(() {
wallpaperBytes = value;
wallpaper = Image.memory(wallpaperBytes);
precacheImage(wallpaper.image, context);
isButtonDisabled=false;
});
});
}
#override
Widget build(BuildContext context) {
return Sizer(
builder: (context, orientation, deviceType) {
return GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: Scaffold(
body: AnimationLimiter(
child: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/background-marmura.jpeg"), fit: BoxFit.cover)),
child: SafeArea(
child: SingleChildScrollView(
child: Container(
margin: const EdgeInsets.fromLTRB(20, 0, 20, 0),
child: Column(
children: [
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Row(
children: [
Flexible(
child: Text(_textFieldController.text,
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 21,
color: Color(0xff393432),
),
),
),
IconButton(
icon: const Icon(
Icons.edit,
size: 21,
color: Color(0xff393432),
),
onPressed: () {
showAlertDialog(context, setState);
},
)
]);
}
),
const SizedBox(height: 5),
GridView.builder(
clipBehavior: Clip.none,
physics: const ScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: SizeConfig.screenWidth!/3,),
itemCount: (visions.length == 12) ? visions.length : visions.length + 1,
itemBuilder: (BuildContext ctx, index) {
if (index < visions.length) {
return AnimationConfiguration.staggeredGrid(
position: index,
duration: const Duration(milliseconds: 1000),
columnCount: 3,
child: ScaleAnimation(
child: FadeInAnimation(
child: OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: index < visions.length, //bind with the boolean
child: Stack(
alignment: Alignment.center,
children: [
Container(
width: 25.w,
height: 25.w,
decoration: BoxDecoration(
image: DecorationImage(
image: CleverCloset.imageFromBase64String(visions[index]).image,
fit: BoxFit.fill),
borderRadius: const BorderRadius.all(
Radius.circular(
5.0) // <--- border radius here
),
),
),
Positioned(
top:0,
right:0,
child: ClipOval(
child: InkWell(
onTap: () {
_removeVisions(index);
},
child: Container(
padding: const EdgeInsets.all(5),
color: Colors.white,
child:
const Icon(
Icons.delete,
size: 16,
color: Color(0xff393432)),
),
),
),
),
],
clipBehavior: Clip.none,
),
),
),
),
);
}
else if(index<12){
return InkWell(
onTap: () {
_openGallery(context);
},
child:
Stack(
alignment: Alignment.center,
children:[
Container(
width: 25.w,
height: 25.w,
decoration:
BoxDecoration(
border: Border.all(color: const Color(0xff393432)),
borderRadius: const BorderRadius.all(Radius.circular(5.0)),
),
child:
const Icon(
Icons.add,
size: 25,
color: Color(0xff393432),
)
),
],
),
);
}
else {
return Container(color: Colors.red);
}
}
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: const [
Text("You can add up to 12 pictures.",
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
fontStyle: FontStyle.italic,
),),
],
),
const SizedBox(height: 10),
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column (
children: [
if(visions.isNotEmpty && visions.length>1 && !isWallpaperCreated) Row(
children: [
SizedBox(
width: 50.w,
child: OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value)
enabled: !isWallpaperCreated &&
visions.isNotEmpty && visions.length > 1,
child: OutlinedButton(
onPressed: visions.isNotEmpty &&
visions.length > 1 ? () async{
setState(() {
wallpaperButtonText = "CREATING...";
//_createWallpaper();
});
wallpaperBytes = await CreateWallpaperLayouts().createWallpaper(visions);
setState(() {
noOfImages = visions.length;
isWallpaperCreated = true;
createWallpaper = true;
});
//Navigator.pushReplacementNamed(context, '/masterclasses');
} : null,
style: OutlinedButton.styleFrom(
primary: const Color(0xffE4BCB4),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
18.0),
),
side: const BorderSide(
width: 3, color: Color(0xffE4BCB4)),
),
child: Text(
wallpaperButtonText,
style: const TextStyle(
color: Color(0xff393432),
fontSize: 14,
fontWeight: FontWeight.w700,
)
),
),
),
),
],
),
const SizedBox(height:40),
if(createWallpaper==true) OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: createWallpaper, //bind with the boolean
child: Row(
children: const [
Flexible(
child: Text("Wallpaper",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 21,
color: Color(0xff393432),
),
),
),
],
),
),
if(createWallpaper==true) const SizedBox(height:15),
if(createWallpaper==true)
OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: createWallpaper,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children:[ Container(
width: 50.w,//double.infinity,
height: 50.h,//SizeConfig.screenHeight,
decoration: BoxDecoration(
image: DecorationImage(
image: Image.memory(wallpaperBytes).image,
fit: BoxFit.fill,
)
),
//child: CreateWallpaperLayouts().createWallpaper(visions),
),],
),
),
if(createWallpaper==true) const SizedBox(height:10),
if(createWallpaper==true) StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: createWallpaper,
child: Row(
children: [
SizedBox(
width: 50.w,
child: OutlinedButton(
onPressed: () {
_saveWallpaper(wallpaperBytes);
setState(() {
saveWallpaperButtonText = "SAVED!";
});
Future.delayed(const Duration(milliseconds: 1300), () {
setState(() {
saveWallpaperButtonText = "SAVE";
});
});
//Navigator.pushReplacementNamed(context, '/masterclasses');
},
style: OutlinedButton.styleFrom(
primary: const Color(0xffE4BCB4),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
side: const BorderSide(width: 3, color: Color(0xffE4BCB4)),
),
child: Text(
saveWallpaperButtonText,
style: const TextStyle(
color: Color(0xff393432),
fontSize: 14,
fontWeight: FontWeight.w700,
)
),
),
),
const SizedBox(width: 10),
],
),
);
}
),
if(createWallpaper==true) const SizedBox(height:50),
Row(
children: const [
Flexible(
child: Text("Goals & Affirmations",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 21,
color: Color(0xff393432),
),
),
),
],
),
],
);
}
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: const [
Text("Add goals and affirmations.",
style: TextStyle(
fontSize: 15,
),),
],
),
const SizedBox(height: 10),
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
children: [
Card(
elevation: 0,
color: const Color(0xffEFEFEF),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: TextFormField(
key: _formKey,
controller: _goalsController,
maxLines: 8,
decoration: const InputDecoration(border: InputBorder.none),
),
)
),
const SizedBox(height: 10),
Row(
children: [
Container(
width: 50.w,
margin: const EdgeInsets.fromLTRB(0, 0, 0, 40),
child: OutlinedButton(
onPressed: () async{
setState(() {
saveGoalsButtonText = "SAVED!";
});
Future.delayed(const Duration(seconds: 1), () {
setState(() {
saveGoalsButtonText = "SAVE GOALS";
});
});
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
goals = _goalsController.text;
prefs.setString('goals', goals);
});
//Navigator.pushReplacementNamed(context, '/masterclasses');
},
style: OutlinedButton.styleFrom(
primary: const Color(0xffE4BCB4),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
side: const BorderSide(width: 3, color: Color(0xffE4BCB4)),
),
child: Text(
saveGoalsButtonText,
style: const TextStyle(
color: Color(0xff393432),
fontSize: 14,
fontWeight: FontWeight.w700,
)
),
),
),
],
),
],
);
}
),
],
),
),
),
),
),
),
),
);
}
);
}
}

Why is Flutter BloC UI not updating after app restart despite state change in observer?

I concede that there are many questions similar to mine, but I have not found a satisfactory answer from those questions. So I decided to make my own question specifying my problem. I have 3 BloCs in my program each with different purposes. They all share similar problems, as such I will ask on one of those BloCs with the hope that one solution will fix all of the BloCs.
The problem is this, if I just started the application and have logged in, the BloC will update the UI. If I have logged in, exited the app, and restarted it, the Bloc will not update the UI. The Bloc in question is called DetailpersonilBloc with 1 event called Detail and 2 states called DetailpersonilInitial and Loaded. At the event of Detail, the state Loaded should be emitted.
I called Detail at LoginPage and at GajiPage at initState. This works when I just opened the app, but does not work when I restart the app. I also have equatable thinking that it will help me but apparently it changes nothing.
Note: The "..." at the GajiPage is just some code that I believe is not necessary for reproduction.
DetailpersonilBloc
part 'detailpersonil_event.dart';
part 'detailpersonil_state.dart';
class DetailpersonilBloc
extends Bloc<DetailpersonilEvent, DetailpersonilState> {
DetailpersonilBloc() : super(const DetailpersonilInitial()) {
on<Detail>((event, emit) async {
SharedPreferences pref = await SharedPreferences.getInstance();
String name = pref.getString('nama');
String nrp = pref.getString('NRP');
String pangkat = pref.getString('pangkat');
String jabatan = pref.getString('jabatan');
String satker = pref.getString('satker');
String polda = pref.getString('polda');
String npwp = pref.getString('NPWP');
String rekening = pref.getString('rekening');
String bank = pref.getString('bank');
emit(Loaded(
name,
nrp,
pangkat,
jabatan,
satker,
polda,
npwp,
rekening,
bank,
));
});
}
}
DetailpersonilEvent
part of 'detailpersonil_bloc.dart';
#immutable
abstract class DetailpersonilEvent extends Equatable {}
class Detail extends DetailpersonilEvent {
#override
List<Object> get props => [];
}
DetailpersonilState
part of 'detailpersonil_bloc.dart';
#immutable
abstract class DetailpersonilState extends Equatable {
final String nama;
final String nrp;
final String pangkat;
final String jabatan;
final String satker;
final String polda;
final String npwp;
final String rekening;
final String bank;
const DetailpersonilState(
{this.nama,
this.nrp,
this.pangkat,
this.jabatan,
this.satker,
this.polda,
this.npwp,
this.rekening,
this.bank});
}
class DetailpersonilInitial extends DetailpersonilState {
const DetailpersonilInitial()
: super(
nama: 'Nama',
nrp: 'NRP',
pangkat: 'Pangkat',
jabatan: 'Jabatan',
satker: 'Satker',
polda: 'Polda',
npwp: 'NPWP',
rekening: 'No Rekening',
bank: 'Nama Bank',
);
#override
List<Object> get props =>
[nama, nrp, pangkat, jabatan, satker, polda, npwp, rekening, bank];
}
class Loaded extends DetailpersonilState {
const Loaded(
String nama,
String nrp,
String pangkat,
String jabatan,
String satker,
String polda,
String npwp,
String rekening,
String bank,
) : super(
nama: nama,
nrp: nrp,
pangkat: pangkat,
jabatan: jabatan,
satker: satker,
polda: polda,
npwp: npwp,
rekening: rekening,
bank: bank);
#override
List<Object> get props =>
[nama, nrp, pangkat, jabatan, satker, polda, npwp, rekening, bank];
}
LoginPage
class LoginPage extends StatefulWidget {
const LoginPage({Key key}) : super(key: key);
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
double width = 0;
double height = 0;
TextEditingController nrpController = TextEditingController();
TextEditingController sandiController = TextEditingController();
final formKey = GlobalKey<FormState>();
DetailpersonilBloc detailPersonilBloc;
GajiBloc gajiBloc;
TunkinBloc tunkinBloc;
bool passwordVisible = false;
#override
void initState() {
super.initState();
checkToken();
}
void checkToken() async {
SharedPreferences pref = await SharedPreferences.getInstance();
if (pref.getString('token') != null) {
detailPersonilBloc = DetailpersonilBloc();
gajiBloc = GajiBloc();
tunkinBloc = TunkinBloc();
detailPersonilBloc.add(Detail());
gajiBloc.add(Gaji());
tunkinBloc.add(Tunkin());
Navigator.push(
context, MaterialPageRoute(builder: (context) => const MainPage()));
}
}
onLogin(DetailpersonilBloc detailPersonilBloc) async {
if (formKey.currentState.validate()) {
var token = await APIService.generateToken(
nrpController.text, sandiController.text);
if (token != null) {
SharedPreferences pref = await SharedPreferences.getInstance();
await pref.setString('token', token.token);
var detail = await APIService.getDetailPersonil(token.token);
await pref.setString('nama', detail.nMPEG);
await pref.setString('NRP', detail.nRP);
await pref.setString('pangkat', detail.nMGOL1);
await pref.setString('jabatan', detail.sEBUTJAB);
await pref.setString('satker', detail.nMSATKER);
await pref.setString('polda', detail.nMUAPPAW);
await pref.setString('NPWP', detail.nPWP);
await pref.setString('rekening', detail.rEKENING);
await pref.setString('bank', detail.nMBANK);
nrpController.clear();
sandiController.clear();
detailPersonilBloc.add(Detail());
Navigator.push(
context, MaterialPageRoute(builder: (context) => const MainPage()));
} else {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Error'),
content: Text('Login Gagal'),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Tutup'),
),
],
);
},
);
}
}
}
#override
Widget build(BuildContext context) {
var detailPersonilBloc = BlocProvider.of<DetailpersonilBloc>(context);
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height;
return Scaffold(
body: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
Opacity(
opacity: 0.5,
child: Image(
image: AssetImage('images/bg-map-min.png'),
),
),
],
),
SingleChildScrollView(
padding: EdgeInsets.only(top: 100),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 100,
width: width,
child: const Image(
image: AssetImage('images/login-logo.png'),
alignment: Alignment.center,
),
),
Container(
padding: const EdgeInsets.all(15),
child: const Text(
'GAJI DAN TUNKIN',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
Form(
key: formKey,
child: Column(
children: [
Container(
margin: const EdgeInsets.all(20 - 2.6),
child: Card(
elevation: 10,
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.only(bottom: 20),
child: const Text(
'LOGIN',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: const EdgeInsets.only(bottom: 25),
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan NRP/NIP';
}
return null;
},
controller: nrpController,
decoration: InputDecoration(
labelText: 'NRP/NIP',
hintText: 'Masukkan NRP/NIP',
prefixIcon: Icon(Icons.person,
color: Colors.blue.shade700),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
TextFormField(
obscureText: !passwordVisible,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan Kata Sandi';
}
return null;
},
controller: sandiController,
decoration: InputDecoration(
labelText: 'Kata Sandi',
hintText: 'Masukkan Kata Sandi',
prefixIcon: Icon(Icons.lock,
color: Colors.blue.shade700),
suffixIcon: IconButton(
onPressed: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
icon: Icon(
passwordVisible
? Icons.visibility
: Icons.visibility_off,
color: Colors.blue.shade700,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
)
],
),
),
),
),
ElevatedButton(
onPressed: () async {
await onLogin(detailPersonilBloc);
},
child: const Text('LOGIN'),
style: ElevatedButton.styleFrom(
primary: Colors.blue.shade700,
minimumSize: const Size(200, 40),
),
)
],
),
),
],
),
),
],
),
);
}
}
GajiPage
class GajiPage extends StatefulWidget {
const GajiPage({Key key}) : super(key: key);
#override
_GajiPageState createState() => _GajiPageState();
}
class _GajiPageState extends State<GajiPage> {
double width = 0;
double height = 0;
var currentYear = DateTime.now().year;
var currentMonth = DateTime.now().month;
DetailpersonilBloc detailPersonilBloc;
GajiBloc gajiBloc;
#override
void initState() {
setState(() {
detailPersonilBloc = DetailpersonilBloc();
detailPersonilBloc.add(Detail());
setMonth();
setYear();
gajiBloc = GajiBloc();
gajiBloc.add(Gaji());
});
super.initState();
}
#override
Widget build(BuildContext context) {
var detailPersonilBloc = BlocProvider.of<DetailpersonilBloc>(context);
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Image(
image: const AssetImage('images/header-logo.png'),
width: width / 2,
),
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(255, 170, 177, 175),
Color.fromARGB(255, 197, 217, 212)
],
),
),
),
),
body: Stack(
children: [
BlocBuilder<GajiBloc, GajiState>(
builder: (context, state) {
return state is GajiLoaded
? ListView(
children: [
Container(
height: 100,
),
Card(
color: const Color.fromARGB(255, 74, 50, 152),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.all(10),
child: const Text(
'Gaji Bersih',
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
),
),
Container(
margin: const EdgeInsets.all(10),
child: Text(
NumberFormat.currency(
locale: 'en',
symbol: 'RP ',
decimalDigits: 0)
.format(state.bersih),
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 40,
color: Colors.white,
),
),
),
],
),
),
Card(
child: Column(
children: [
Container(
color: const Color.fromARGB(255, 238, 238, 238),
width: width,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Container(
margin: const EdgeInsets.fromLTRB(
10, 10, 0, 10),
width: (width / 2) - 25,
child: const Text(
'Detail Gaji',
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
),
),
),
Container(
margin: const EdgeInsets.fromLTRB(
5, 10, 20, 10),
width: (width / 2) - 18,
child: Text(
'${state.bulan} - ${state.tahun}',
textAlign: TextAlign.end,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
),
),
)
],
),
),
...
],
),
),
Container(
height: 50,
),
],
)
: Center(
child: Text(
'Tidak ada data. Data gaji bulan ${state.bulan} belum diproses'),
);
},
),
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Center(
child: BlocBuilder<DetailpersonilBloc, DetailpersonilState>(
builder: (context, state) {
return Card(
child: Row(
children: [
Container(
margin: const EdgeInsets.all(10),
child: const CircleAvatar(
backgroundImage: AssetImage('images/Profpic.PNG'),
radius: 30,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 250,
padding: const EdgeInsets.all(5),
child: Text(
state.nama,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold),
)),
Container(
padding: const EdgeInsets.all(5),
child: Text(
state.nrp,
style: const TextStyle(fontSize: 15),
)),
],
),
GestureDetector(
onTap: () {
detailPersonilBloc.add(Detail());
showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) => detailsBottomSheet(),
);
},
child: const Text(
'DETAILS',
style: TextStyle(color: Colors.blue),
),
)
],
),
);
},
),
),
GestureDetector(
onTap: () {
showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, StateSetter setState) {
return filterBottomSheet();
},
);
},
);
},
child: Container(
height: 50,
width: width,
decoration: const BoxDecoration(
color: Color.fromARGB(255, 244, 244, 244),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
margin: const EdgeInsets.only(right: 3),
child: const Icon(
Icons.tune,
color: Color.fromARGB(255, 45, 165, 217),
),
),
const Text(
'Filter',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20,
color: Color.fromARGB(255, 45, 165, 217),
),
),
],
),
),
)
],
)
],
),
);
}
}
Note 2: The "..." is a bunch of code not needed for reproduction.
The answer is surprisingly simple as I would learn from my internship. The reason the Bloc is not updating is because I was not using the context of the application. So when I generated a new Bloc inside my pages, it was "empty". So the solution is to use context.read().add(BlocEvent()) or create a new bloc with BlocProvider.of(context) then add the event. Basically the bloc has to be provided with the original context of the application.

Using provider between different screens and appBar in flutter

I'm using Provider in my flutter app. I was able to change one widget with another one, without using set state. That was great!
My feature consist that when I tap in the button 'Añadir al carrito', a number over the shopping car change, increasing one by one.
This is the part of my code that I put in the button, that is inside the body of my scaffold using the provider.of:
final _numCompras = Provider.of<MyShoppingCart>(context);
return Padding(
padding: const EdgeInsets.only(top: 15.0),
child: GestureDetector(
onTap: () {
_numCompras.numCompras = _numCompras.numCompras + 1;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('Producto Añadido'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: '',
onPressed: () {
},
),
));
},
child: Container(
height: 80,
width: double.infinity,
decoration: BoxDecoration(
color: Constants.kColorRojo,
borderRadius: BorderRadius.circular(60),
border: Border.all(width: 1, color: Colors.black),
),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Añadir al Carrito',
style: TextStyle(
color: Colors.white,
fontFamily: 'Poppins',
fontSize: 20,
),
),
SizedBox(
width: 15,
),
Icon(
Icons.shopping_cart_outlined,
color: Colors.white,
size: 30,
),
],
),
),
),
),
);
}
This is my class using ChangeNotifier
int _numCompras = 0;
get numCompras => _numCompras;
set numCompras(int newValue) {
_numCompras = newValue;
notifyListeners();
}
}
And in my main.dart I`m putting the ChangeNotifierProvider, because I thing that one contain all of the rest widgets.
class JuniorsApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<MyShoppingCart>(
create: (context) => MyShoppingCart(),
//builder: ,
)
],
child: MaterialApp(
theme: ThemeData.light().copyWith(
primaryColor: Color(0xFFd51921),
scaffoldBackgroundColor: Color(0xFFf2f2f2),
),
home: PageStart(),
initialRoute: PageStart().ID_PageStart,
routes: {
page_IniciarSesion().ID_PageIniciarSesion: (context) =>
page_IniciarSesion(),
PageStart().ID_PageStart: (context) => PageStart(),
RegistroNombrePage().ID_PageRegistroNombre: (context) =>
RegistroNombrePage()
},
debugShowCheckedModeBanner: false,
),
);
}
}
And finally the code of my appBar that is the widget that I`m changing:
Widget build(BuildContext context) {
bool _hasBackButton = hasBackArrow ?? true;
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
return Selector<MyShoppingCart,int>(
selector: (_, model) => model.numCompras,
builder: (context, numCompras, _) {
return SafeArea(
child: Material(
elevation: 15,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (_hasBackButton)
RoundedIconButton(
iconData: Icons.arrow_back_ios,
press: () {
Navigator.pop(context, MaterialPageRoute(
builder: (context) {
return Selector(builder: (context, numCompras, _){}, selector: (_, model) => model.numCompras);
},
)
);
}
),
Text(
this.title == null ? 'Junior \'s Tacos' : this.title,
style: Constants.kFuenteAppBar,
),
this.hasCart
? _widgetShoppingCar(context, height, width, numCompras)
: Spacer() ,
],
),
),
),
);
},
);
}
Widget _widgetShoppingCar(context, height, width, _numCompras) {
return Stack(
children: [
RoundedIconButton(
iconData: Icons.shopping_cart_outlined,
press: () {
_viewShop = !_viewShop;
this.changeAppBarShop(_viewShop);
/*Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CartPage(),
),
);*/
},
),
Positioned(
bottom: height * 0.04,
left: width * 0.09,
child: Container(
width: width * 0.05,
height: height * 0.025,
decoration: BoxDecoration(
color: Colors.red[500],
border: Border.all(
color: Colors.red[500],
),
borderRadius: BorderRadius.all(Radius.circular(20))),
child: Center(
child: Text(
'${_numCompras}',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
)),
),
)
],
);
}
The thing is that it works fine, but when I do Navigator.pop from the appBar. The count over the shopping car come back to zero.
Here is how its working at the moment
Could anyone give a me a tip about this?
thanks!
Well, if anyone have this kind of problem. The issue for me was that I have two ChangeNotifierProvider. One in my main.dart and the other one in my widget that contain the button. I remove the last one and now its working.

inavlid argument(s) /Exception caught by widgets library

I have an empty card widget which I used it for opening adding page, my problem is when I pushed from main categories screen I can add main category, but When I used this empty card button on sub category screen of any main category, add screen is opening but when I pushed add I took invalid argument(s) error app is broken. How can ı solve this issue?.
class EmptyCard extends StatelessWidget {
EmptyCard({this.where});
final String where;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top:15.0),
child: TextButton(
onPressed: (){
if(where=="homeScreen"){
showModalBottomSheet(
context: context,
builder: (BuildContext context)=> AddMenuScreen(buttonText: "Menü Ekle",route:where),
);
}
else if(where=="subCategoryScreen"){
showModalBottomSheet(
context: context,
builder: (BuildContext context)=> AddMenuScreen(buttonText: "Tarif Ekle",route:where),
);
}
},
child: Container(
height: 180,
width: 180,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.black12.withOpacity(0.1),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.add_circle_outline_outlined,size: 100,color: Colors.grey.shade400,),
],
),
),
),
);
}
}
class AddMenuScreen extends StatefulWidget {
AddMenuScreen({this.categoryId,this.buttonText, this.route});
final int categoryId;
final String route;
final String buttonText;
static String id="addMenuScreen";
#override
_AddMenuScreenState createState() => _AddMenuScreenState();
}
class _AddMenuScreenState extends State<AddMenuScreen> {
final stickerList= List<String>.generate(23, (index) => "images/sticker$index");
int selectedIndex=-1;
Color _containerForStickersInactiveColor=Colors.white;
Color _containerForStickersActiveColor=Colors.black12;
String chosenImagePath;
String newCategoryName;
int addScreenImageNum;
#override
Widget build(BuildContext context) {
return Material(
child: Container(
color: kColorTheme9,
child: Container(
height: 400,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(topRight: Radius.circular(40),topLeft: Radius.circular(40)),
),
child:Padding(
padding:EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
color: kColorTheme2,
borderRadius: BorderRadius.circular(90)
),
child: TextField(
style: TextStyle(
color: Colors.black,
fontFamily:"Graduate",
fontSize: 20,
),
textAlign: TextAlign.center,
onChanged: (value){
newCategoryName=value;
},
decoration: InputDecoration(
border:OutlineInputBorder(
borderRadius: BorderRadius.circular(90),
borderSide: BorderSide(
color: Colors.teal,
),
),
hintText: "Menü ismi belirleyin",
hintStyle: TextStyle(
color: Colors.black.withOpacity(0.2),
fontFamily: "Graduate",
),
),
),
),
SizedBox(height: 20,),
Text(" Yana kadırarak menünüz icin bir resim secin",textAlign: TextAlign.center,
style: TextStyle(fontFamily: "Graduate", fontSize: 12),),
SizedBox(height: 20,),
Expanded(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: stickerList.length,
itemBuilder: (context,index){
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: index == selectedIndex ?
_containerForStickersActiveColor :
_containerForStickersInactiveColor,
),
child:TextButton(
child: Image(
image: AssetImage("images/sticker$index.png"),
),
onPressed: (){
setState(() {
selectedIndex = index;
});
},
),
);
}
),
),
SizedBox(height: 20,),
Container(
decoration: BoxDecoration(
border: Border.all(style: BorderStyle.solid),
color: kColorTheme7,
borderRadius: BorderRadius.circular(90),
),
child: TextButton(
onPressed: (){
if(widget.route=="homeScreen"){
Navigator.pop(context, ModalRoute.withName(HomeScreen.id));
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=>HomeScreen(newCategoryName: newCategoryName,
newCategoryImagePath: "images/sticker$selectedIndex.png")));
}
else if(widget.route=="subCategoryScreen"){
Navigator.pop(context, ModalRoute.withName(SubCategoriesScreen.id));
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=>SubCategoriesScreen(categoryId: widget.categoryId,subCategoryName: newCategoryName,
subCategoryImagePath:"images/sticker$selectedIndex.png" )));
}
},
child: Text(widget.buttonText, style: TextStyle(fontSize: 20, color: Colors.white,
fontFamily: "Graduate", fontWeight: FontWeight.bold),),
),
),
],
),
),
),
),
);
}
}
class _SubCategoriesScreenState extends State<SubCategoriesScreen> {
void initState(){
super.initState();
if(widget.categoryId!=null && widget.subCategoryImagePath!=null && widget.subCategoryName!=null){
addSubCategory();
}
}
void addSubCategory(){
SubCategoryModel subCategoryModel=SubCategoryModel(
subCategoryId:widget.categoryId,
subCategoryImagePath: widget.subCategoryImagePath,
subCategoryName: widget.subCategoryName,
categoryColor: Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(0.5));
if(categoryModels!=null){
for (var categoryModel in categoryModels){
if (categoryModel.categoryId == subCategoryModel.subCategoryId){
categoryModel.subCategoryModels.insert(categoryModel.subCategoryModels.length-1,subCategoryModel);
}
}
}
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
centerTitle: true,
title: BorderedText(
child:Text(
categoryModels[widget.categoryId].categoryName,
style: TextStyle(
color: Color(0XFFFFFB00),
fontSize: 30,
fontFamily: "Graduate"
),
),
strokeWidth: 5,
strokeColor: Colors.black,
),
elevation: 5,
backgroundColor: Color(0xFFF2C3D4).withOpacity(1),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: (){
Navigator.pop(context);
},
iconSize: 40,
color: Color(0xFFA2000B),
),
actions: [
CircleAvatar(
radius: 27,
backgroundColor: Colors.transparent,
backgroundImage: AssetImage("images/cuttedlogo.PNG"),
)
],
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("images/logoBGopacity.png"),
fit: BoxFit.cover,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: GridView.builder(
scrollDirection: Axis.vertical,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount:categoryModels[widget.categoryId].subCategoryModels.length,
itemBuilder: (context,index){
if(categoryModels[widget.categoryId].subCategoryModels[index].subCategoryId==-1){
return EmptyCard(where: "subCategoryScreen");
}
return SubCategoryCard(subCategoryId:widget.categoryId,subcategoryName: categoryModels[widget.categoryId].subCategoryModels[index].subCategoryName,
subCategoryImagePath: categoryModels[widget.categoryId].subCategoryModels[index].subCategoryImagePath,
subCategoryColor: categoryModels[widget.categoryId].subCategoryModels[index].categoryColor,);
}
),
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: EdgeInsets.all(10),
child: Container(
decoration: BoxDecoration(
border: Border.all(style: BorderStyle.solid),
color: kColorTheme7,
borderRadius: BorderRadius.circular(40),
),
child: TextButton(
onPressed: (){
showModalBottomSheet(
context: context,
builder: (BuildContext context)=> AddMenuScreen(categoryId:widget.categoryId,buttonText: "Tarif Ekle", route:"subCategoryScreen"),
);
},
child: BorderedText(
strokeWidth: 5,
strokeColor: Colors.black,
child:Text("Tarif Ekle",style: TextStyle(
color: Colors.white,
fontFamily:'Graduate',
fontSize:30,
),
),
),
),
),
),
],
)
],
),
),
),
);
}
}
Error:
======== Exception caught by widgets library =======================================================
The following ArgumentError was thrown building SubCategoriesScreen(dirty, state: _SubCategoriesScreenState#72f75):
Invalid argument(s)
The relevant error-causing widget was:
SubCategoriesScreen file:///C:/Users/yagiz/AndroidStudioProjects/lezzet_kitabi/lib/screens/add_menu_screen.dart:121:98
When the exception was thrown, this was the stack:
#0 List.[] (dart:core-patch/growable_array.dart:254:60)
#1 _SubCategoriesScreenState.build (package:lezzet_kitabi/screens/subCategoriesScreen.dart:61:29)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4742:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4625:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4797:11)

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!