How do I get a black banner with text to dynamically show depending upon what a user does on another screen in Flutter? - flutter

I am tasked with the challenge of getting a black banner to show when a user returns from a screen on that same tab upon ordering an item on another screen. The current code below only shows the black banner appropriately if the user leaves by selecting another tab then returns to the screen. The part that I need to be dynamically presented starts with "if (OrderProvider.listOrderSummary.isNotEmpty)". I believe that this involves making a stateless widget stateful, but that results in errors that suggest that may not be the right approach.
class GridItems extends StatelessWidget {
GridItems({#required this.infinityPageController});
final InfinityPageController infinityPageController;
#override
Widget build(BuildContext context) {
final List<Item> itemList = Provider.of<List<Item>>(context);
if (itemList == null) {
return Center(
child: CupertinoActivityIndicator(
radius: 20,
),
);
}
final List<Item> mapItemList = itemList.toList();
return Column(
children: <Widget>[
TopMenuNavigation(
infinityPageController: infinityPageController,
title: 'ALL ITEMS',
),
Divider(
color: Colors.grey,
),
Expanded(
child: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: itemList.length,
itemBuilder: (BuildContext contex, int index) {
return Center(
child: InkWell(
onTap: () {
Navigator.of(context)
.pushNamed('/order', arguments: mapItemList[index]);
},
child: Container(
width: 310,
margin: EdgeInsets.all(7),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(width: 1, color: Colors.grey),
borderRadius: BorderRadius.all(
Radius.circular(7.0),
),
),
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 10, top: 10),
width: double.infinity,
child: Text('${mapItemList[index].itemName}'),
),
Expanded(
child: Image.network(
'${mapItemList[index].imageUrl}',
),
),
],
),
),
),
);
},
),
),
if (OrderProvider.listOrderSummary.isNotEmpty)
GestureDetector(
onTap: () {
Navigator.of(context).pushNamed('/review_order');
},
child: Container(
height: 55,
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Spacer(),
Text(
'REVIEW ORDER (${OrderProvider.listOrderSummary.length} items)',
style: TextStyle(
fontSize: 17,
color: Colors.white,
fontFamily: 'OpenSans',
fontWeight: FontWeight.bold),
),
SizedBox(width: 8),
Icon(
Icons.navigate_next,
color: Colors.white,
size: 35,
),
SizedBox(
width: MediaQuery.of(context).size.height * 0.065,
),
],
),
),
)
else
SizedBox(),
],
);
}
}

You have to somehow rebuild your screen (or part of it) where you want to return to. This is where a state-management solution should come into place :).
Create a BLoC or a ValueNotifier and rebuild the widget with a StreamBuilder or a ValueListenableBuilder. If you want to make it simple just create a shared state inside a stateful widget between your tab screens and call setState if the black banner should appear

Related

pathing argument from widget to another screen

is there a way to path this "model" from commentItem Widget to a Navigator that transmit me to another screen ?
like Navigator.push or Navigator.pushNamed ?
it will help me to display posts or comments in different screens like home screen it will display posts
as well as my profile screen it will display only my posts so i need to know how to path arguments to different screens by Navigator
how to perform this with code ?
Widget commentItem(CommentModel model, context,) => Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
radius: 22,
backgroundImage: NetworkImage('${model.image}'),
),
SizedBox(
width: 15,
),
Expanded(
child: Column(
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: SocialCubit.get(context).iconChangeTheme? Colors.white70 : Colors.grey[350],
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'${model.name}',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: SocialCubit.get(context).iconChangeTheme? Colors.black : Colors.black
),
),
SizedBox(
width: 7,
),
Text(
'${model.dateTime}',
style: TextStyle(
fontSize: 11,
color: SocialCubit.get(context).iconChangeTheme? Colors.black : Colors.black
),
),
],
),
if(model.text != '')
SizedBox(
height: 5,
),
Text('${model.text}'
,style: TextStyle(
color: SocialCubit.get(context).iconChangeTheme? Colors.black : Colors.black
),),
],
),
),
),
if (model.commentImage != '')
Padding(
padding: const EdgeInsetsDirectional.only(top: 5),
child: Container(
height: 170,
//width: double.infinity,
alignment: Alignment.topLeft,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image(image: NetworkImage('${model.commentImage}'),
//fit: BoxFit.fill,
alignment: Alignment.topLeft),
),
),
),
],
),
),
],
),
],
);
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => commentItem(SocialCubit.get(context).comments[index], context,),
separatorBuilder: (context, index) => SizedBox(height: 20),
itemCount: SocialCubit.get(context).comments.length),
You can use the Navigator push as following:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreenForPosts(
postCode: postCode,
week: week,
postName: postName
)));
Your class will look like following:
class NewScreenForPosts extends StatelessWidget {
final postCode;
final week;
final postName;
const NewScreenForPosts({Key? key, required this.postCode, required this.week, required this.postName}) : super(key: key);
#override
Widget build(BuildContext context) {
return const Placeholder();
}
}
if using Stateless widget, you can access the passed data through Navigator directly like postCode, week or postName etc. If using Stateful, you need to use widget.postCode, widget.week or widget.postName notation.
using Getx Package
Get.to(ScreenName());
https://pub.dev/packages/get

Flutter 1 positional argument expected, but 0 found

I have created a page with a side drawer which when pressed on the magnifying glass icon opens search requirements for different recipes e.g vegetarian, gluten free etc. I want the recipes to be shown on the page once the side drawer is closed.
I have created a widget called RecipeList() which has all of the code to retrieve the recipes from API and works perfectly on its own page without a side drawer. I wish to call this RecipeList() widget so that when the side drawer is closed they are displayed.
I'm having an error on the 7th line from the bottom, calling the RecipeList() widget it says "1 positional argument(s) expected, but 0 found.Try adding the missing arguments." I feel like I'm missing something that may be pretty obvious to fresh eyes?
Please see this images as references in case I didn't explain it well enough :)
HOW I WISH FOR THE RECIPES TO BE DISPLAYED WHEN THE SIDE BAR IS CLOSED (THIS WORKS ON A PAGE WITHOUT THE SIDE BAR):
CURRENTLY HOW THE SIDE BAR OPENS:
THE PAGE I WISH FOR THE RECIPES TO BE DISPLAYED ON:
//SIDE DRAWER BUILD
Widget build (BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
body: SwipeDrawer(
radius: 20,
key: drawerKey,
hasClone: false,
bodyBackgroundPeekSize: 30,
backgroundColor: Colors.white,
drawer: buildDrawer(),
child: buildBody(),
),
);
}
//WIDGET RESPONSIBLE FOR SHOWING RECIPES FROM API
Widget RecipeList(BuildContext context) {
return Scaffold(
body:
mapResponse==null?Container(): SingleChildScrollView(
child:Column(
children: <Widget> [
ListView.builder(
//SHRINKWRAP ADJUSTS THE BOXES TO FIT THE SCREEN
shrinkWrap: true,
itemBuilder: (context,index){
return Container(
//RECIPE PAGE LAYOUT DESIGN
//EACH RECIPE IS SHOWN WITH ITS TITLE & CALORIES IN A LITTLE RECTANGLE WITH IMAGE
//SETTING UP EACH RECIPE RECTANGLE PLACE HOLDER
margin: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
width: MediaQuery.of(context).size.width,
height: 180,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.6),
offset: Offset(0.0,10.0,),
blurRadius: 10.0,
spreadRadius: -6.0,
),
],
//EACH RECIPES IMAGE GOES HERE
image: DecorationImage(
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.35),
BlendMode.multiply,
),
//IMAGE IS FETCHED FROM API
image: NetworkImage(listOfResults[index]['image']),
fit: BoxFit.cover,
),),
//EACH RECIPES TITLE IS HERE
child: Stack(
children: [
Align(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 5.0),
child: Text(
//TITLE IS FETCHED FROM API
listOfResults[index]['title'],
style: TextStyle(
fontSize: 19,
color: Colors.white.withOpacity(1.0)
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
textAlign: TextAlign.center,
),
),
alignment: Alignment.center,
),
//EACH RECIPES CALORIES IS HERE
Align(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: EdgeInsets.all(5),
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.4),
borderRadius: BorderRadius.circular(15),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(Icons.star,color: Colors.yellow,size: 18),
SizedBox(width: 7),
Text(
//CALORIES IS FETCHED FROM API
listOfResults[index]['spoonacularScore'].toString(),
style: TextStyle(
color: Colors.white.withOpacity(1.0)),
),
],
),
),
Container(
padding: EdgeInsets.all(5),
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.4),
borderRadius: BorderRadius.circular(15),
),
child: Row(
children: [
Icon(
Icons.schedule,
color: Colors.yellow,
size: 18,
),
SizedBox(width: 7),
Text(listOfResults[index]['readyInMinutes'].toString(),
style: TextStyle(
color: Colors.white.withOpacity(1.0)),),
],
),
)
],
),
alignment: Alignment.bottomLeft,
)]
) );
},
//IF THE LIST OF RESULTS RETRIEVED FROM API IS NONE, ITEMCOUNT IS EQUAL TO 0. OTHERWISE ITEMCOUNT IS EQUAL TO LENGTH OF THE LIST OF RESULTS
itemCount: listOfResults==null ? 0 : listOfResults.length)
],
),
));
}
//DISPLAY RECIPES ON PAGE SCREEN FROM API SEARCH
Widget buildBody() {
return Column(
children: [
// build your appBar
AppBar(
title: Text('Recipes'),
leading: InkWell(
onTap: () {
if (drawerKey.currentState.isOpened()) {
drawerKey.currentState.closeDrawer();
} else {
drawerKey.currentState.openDrawer();
}
},
child: Icon(Icons.search)),
),
//MAIN SCREEN
Expanded(
child: Container(
color: Colors.white,
child: RecipeList(),
),
),
],
);
}
}
The error explanation is that you defined RecipeList like this:
Widget RecipeList(BuildContext context)
And you call constructor like this:
child: RecipeList(),
As you pass no parameter you got this error, you need to suppress positionnali parameter in Widget définition or pass parameter when you instanciate Widget.

How to make ExpansionTile scrollable when end of screen is reached?

In the project I'm currently working on, I have a Scaffold that contains a SinlgeChildScrollView. Within this SingleChildScrollView the actual content is being displayed, allowing for the possibility of scrolling if the content leaves the screen.
While this makes sense for ~90% of my screens, however I have one screen in which I display 2 ExpansionTiles. Both of these could possibly contain many entries, making them very big when expanded.
The problem right now is, that I'd like the ExpansionTile to stop expanding at latest when it reaches the bottom of the screen and make the content within the ExpansionTile (i.e. the ListTiles) scrollable.
Currently the screen looks like this when there are too many entries:
As you can clearly see, the ExpansionTile leaves the screen, forcing the user to scroll the actual screen, which would lead to the headers of both ExpansionTiles disappearing out of the screen given there are enought entries in the list. Even removing the SingleChildScrollView from the Scaffold doesn't solve the problem but just leads to a RenderOverflow.
The code used for generating the Scaffold and its contents is the following:
class MembershipScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => _MembershipScreenState();
}
class _MembershipScreenState extends State<MembershipScreen> {
String _fontFamily = 'OpenSans';
Widget _buildMyClubs() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Color(0xFFD2D2D2),
width: 2
),
borderRadius: BorderRadius.circular(25)
),
child: Theme(
data: ThemeData().copyWith(dividerColor: Colors.transparent),
child: ExpansionTile(
title: Text("My Clubs"),
trailing: Icon(Icons.add),
children: getSearchResults(),
),
)
);
}
Widget _buildAllClubs() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Color(0xFFD2D2D2),
width: 2
),
borderRadius: BorderRadius.circular(25)
),
child: Theme(
data: ThemeData().copyWith(dividerColor: Colors.transparent),
child: SingleChildScrollView(
child: ExpansionTile(
title: Text("All Clubs"),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.add)
],
),
children: getSearchResults(),
),
)
)
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
body: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light,
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Stack(
children: <Widget>[
Container(
height: double.infinity,
width: double.infinity,
decoration: BoxDecoration(
gradient: kGradient //just some gradient
),
),
Center(
child: Container(
height: double.infinity,
constraints: BoxConstraints(maxWidth: 500),
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 40.0, vertical: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Clubs',
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
fontFamily: _fontFamily,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 20,
),
_buildMyClubs(),
SizedBox(height: 20,),
_buildAllClubs()
],
),
),
),
),
],
),
)
),
);
}
List<Widget> getSearchResults() {
return [
ListTile(
title: Text("Test1"),
onTap: () => print("Test1"),
),
ListTile(
title: Text("Test2"),
onTap: () => print("Test2"),
), //etc..
];
}
}
I hope I didn't break the code by removing irrelevant parts of it in order to reduce size before posting it here. Hopefully, there is someone who knows how to achieve what I intend to do here and who can help me with the solution for this.
EDIT
As it might not be easy to understand what I try to achieve, I tried to come up with a visualization for the desired behaviour:
Thereby, the items that are surrounded with dashed lines are contained with the list, however cannot be displayed because they would exceed the viewport's boundaries. Hence the ExpansionTile that is containing the item needs to provide a scroll bar for the user to scroll down WITHIN the list. Thereby, both ExpansionTiles are visible at all times.
Try below code hope its help to you. Add your ExpansionTile() Widget inside Column() and Column() wrap in SingleChildScrollView()
Refer SingleChildScrollView here
Refer Column here
You can refer my answer here also for ExpansionPanel
Refer Lists here
Refer ListView.builder() here
your List:
List<Widget> getSearchResults = [
ListTile(
title: Text("Test1"),
onTap: () => print("Test1"),
),
ListTile(
title: Text("Test2"),
onTap: () => print("Test2"),
), //etc..
];
Your Widget using ListView.builder():
SingleChildScrollView(
padding: EdgeInsets.all(20),
child: Column(
children: [
Card(
child: ExpansionTile(
title: Text(
"My Clubs",
),
trailing: Icon(
Icons.add,
),
children: [
ListView.builder(
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Column(
children: getSearchResults,
);
},
itemCount: getSearchResults.length, // try 50 length just testing
),
],
),
),
],
),
),
Your Simple Widget :
SingleChildScrollView(
padding: EdgeInsets.all(20),
child: Column(
children: [
Card(
child: ExpansionTile(
title: Text(
"My Clubs",
),
trailing: Icon(
Icons.add,
),
children:getSearchResults
),
),
],
),
),
Your result screen ->

How to display loading widget until a main widget is loaded

I am using the animation package to create a popup modal However building the widget inside the model is very noticeably slow and is making making the popup take time to open. I am trying to put a loading indicator when opening the modal then build the widget afterward and just update.
I am lost on how to accomplish that.. it would be highly appreciated if someone could help.
this is the animation package method for the modal
ElevatedButton(
onPressed: () {
showModal<void>(
context: context,
builder: (BuildContext context) {
// building _ExampleAlertDialog takes time
return _ExampleAlertDialog(loading: loading);
},
).then((state) => setState(() => {loading = !loading}));
},
child: const Text('SHOW MODAL'),
),
the _ExampleAlertDialog is supposed to be a listView
class _ExampleAlertDialog extends StatefulWidget {
_ExampleAlertDialog({
this.loading,
});
final bool loading;
#override
__ExampleAlertDialogState createState() => __ExampleAlertDialogState();
}
class __ExampleAlertDialogState extends State<_ExampleAlertDialog> {
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 50, horizontal: 20),
child: Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Material(
child: Column(
children: [
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).dividerColor))),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Icon(Icons.arrow_back), Icon(Icons.refresh)],
),
),
// This is the Listview I am trying to avoid onLoad
Expanded(child: widget.loading == true ? Container():
ListView.builder(
itemCount: 16,
itemBuilder: (BuildContext ctxt, int index) {
return Container(
// width: MediaQuery.of(context).size.width * 1,
child: Row(
children: <Widget>[
Icon(
Icons.radio_button_unchecked,
color: Colors.blue,
size: 12.0,
),
SizedBox(
width: 20.0,
),
Text(
"Travetine",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400),
),
],
),
);
},
)
)
],
),
),
),
),
);
}
}

How to put scroll view inside stack widget in flutter

I am making a flutter application in which i uses body as a stack and in this stack i have two child.One is main body and other is back button which is at top of screen.The first child of stack is scrollview.Here is my build method.
Widget build(BuildContext context) {
return Scaffold(
//debugShowCheckedModeBanner: false,
key: scaffoldKey,
backgroundColor: Color(0xFF5E68A6),
body: Stack(
children: <Widget>[
Container(
margin: const EdgeInsets.fromLTRB(0.0, 10.0 , 0.0 , 0.0 ),
height: double.infinity,
child:CustomScrollView(
slivers: <Widget>[
new Container(
margin: EdgeInsets.all(15.0),
child:Text(getTitle(),
style: TextStyle(fontSize: 20.0,fontWeight: FontWeight.bold,color: Colors.white),
),
),
//middle section
_isLoading == false ?
new Expanded(child: GridView.builder(
itemCount: sub_categories_list.length,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (context, position){
return InkWell(
child: new Container(
//color: Colors.white,
padding: EdgeInsets.all(20),
margin: EdgeInsets.all(10),
height: 130,
width: 130,
child: new Center(
child :
Text(sub_categories_list[position].name,
style: TextStyle(fontSize: 18.0,fontWeight: FontWeight.bold),
)
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(16)),
// border: Border.all(color: Colors.black, width: 3),
),
),
onTap: () {
//write here
// Fluttertoast.showToast(msg: "You clicked id :"+sub_categories_list[position].cat_id.toString());
Navigator.pushNamed(context, '/advicemyself');
},
);
}
))
:
CircularProgressIndicator(),
Container(
margin: EdgeInsets.all(18.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new Column(
children: <Widget>[
Image.asset('assets/bt1.png'),
Container(
margin: EdgeInsets.all(10.0),
child: Text("FIND HELP",
style: TextStyle(fontSize: 18.0,color: Colors.white),
),
)
],
),
new Column(
children: <Widget>[
Image.asset('assets/bt2.png'),
Container(
margin: EdgeInsets.all(10.0),
child: Text("HOME",
style: TextStyle(fontSize: 18.0,color: Colors.white),
),
)
],
),
new Column(
mainAxisAlignment:MainAxisAlignment.spaceEvenly,
children: <Widget>[
Image.asset('assets/bt3.png'),
Container(
margin: EdgeInsets.all(10.0),
child: Text("CALL 999",
style: TextStyle(fontSize: 18.0,color: Colors.white),
),
)
],
),
],
),
),
],
),
),
Positioned(
left: 10,
top: 30,
child: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => {
//go back
},
color: Colors.white,
iconSize: 30,
),
),
// makeview()
],
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
I have also tried using SingleChildScrollView but that also does not works.What i am doing wrong here ?
Here is link to the design which i want to make.
https://imgur.com/a/w7nLmKC
The back should be above scroll view so i used stack widget.
Running your sample code, there doesn't seem to be a need for overlapping widgets. Using Stack seems to be unnecessary. One way you could do is by using Column widget, and using Expanded as you see fit.
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Widget(), // back button goes here
CustomScrollView(...),
],
),
);
}
Otherwise, if you really need to use Stack, the scroll function should work fine. I've tried this locally and the Stack widget doesn't interfere with scrolling of Slivers, ListView, and GridView.
Stack(
children: [
/// Can be GridView, Slivers
ListView.builder(),
/// Back button
Container(),
],
),