How to add one search bar use for two tabs in flutter? - flutter

At my mobile apps homepage i have tabbar with two tabs act as category. both of those tabs display a listview with content depends on the category. (for example, a listview of book with fiction and non-fiction category.) and i want to add a single search function to search a book regardless their category.
The problem is:
One search bar can only search on one category. means the search bar can only search on fiction category. i try to add 2nd search bar in each tab but in result it act like the problem below
that whenever i click on the 2nd tab and then click on search bar, it will automatically go back to first tabs which is the default tab.
Solution that i think of:
Is to add setState so the tabs doesnt move back to default tab whenever i click on the search bar, but i dont know how.
Here is my code:
// Tab Bar MOF and CIDB
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(16),
bottomRight: Radius.circular(16)
),
color: Theme.of(context).primaryColor,
),
width: double.infinity,
child: TabBar(
controller: _tabController,
indicatorSize: TabBarIndicatorSize.tab,
indicatorPadding: EdgeInsets.all(6),
indicator: const UnderlineTabIndicator(borderSide: BorderSide(color: Colors.red, width: 100.0),
insets: EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 42.0),
),
unselectedLabelColor: Colors.grey,
tabs: [
Tab(child: Text('MOF', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w300, ))),
Tab(child: Text('CIDB', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w300)))
]
),
),
SizedBox(
height: 30,
),
//Space for Tender Listview
Expanded(
child: Container(
width: double.infinity,
//Tender List tile
child: TabBarView(
controller: _tabController,
children: [
//MOF List
Column(
children: [
Expanded(
child: ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: MOFs.length,
itemBuilder: (context, index) {
final MOF = MOFs[index];
return Card(
margin: EdgeInsets.fromLTRB(15, 20, 15, 20),
child: InkWell(
onTap: (){},
child: Container(
margin: EdgeInsets.all(4),
height: 200,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12),
//color: Colors.blue
),
child: Center(child: Text(MOF.title)),
),
),
);
}),
),
Container(
//color: Colors.pink,
margin: EdgeInsets.fromLTRB(150, 10, 150, 10),
child: TextField(
controller: controller,
onChanged: searchMOF,
decoration: InputDecoration(
hintText: 'Search by Keyword',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
borderSide: BorderSide(color: Colors.green))
),
),
),
],
),
//CIDB List
Column(
children: [
Expanded(
child: ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: CIDBs.length,
itemBuilder: (context, index) {
final CIDB = CIDBs[index];
return Container(
margin: EdgeInsets.all(10),
height: 200,
width: 50,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20),
//color: Colors.purple
),
child: InkWell(
onTap: (){},
child: Card
(child: Center(child: Text(CIDB.title))),
),
//List testing
);
}),
),
Container(
//color: Colors.pink,
margin: EdgeInsets.fromLTRB(150, 10, 150, 10),
child: TextField(
controller: controller,
onChanged: searchCIDB,
decoration: InputDecoration(
hintText: 'Search by Keyword',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
borderSide: BorderSide(color: Colors.green))
),
),
),
],
),
],
),
),
),
],
),
);
}

You can make the search bar works globally by creating a parent stateful widget with search bar and child widget with tabs and tabs' body. In each tab body, you can check if search text field controller is not empty and if yes, you make only the books that contains search text appears and if no, you make the whole list appears

the whole step is too wide. i'll suggest some outline.
first of all, you have to know behaviour of ListView in flutter:
Creation and Destruction
Only visible children will be rendered in a listview. When a child is
scrolled out of view, the associated element subtree, states and
render objects are destroyed
second is the lifecycle. When you call setState it means flutter re-execute build method. the method below.
#override
Widget build(BuildContext context) {
return
thats why, everytime you call setState, the tabs is back to first tab. because it execute build method from the begining.
how to avoid rebuild entire widget is: Separate into new class.
eg:
class TabMOF extend StatelessWidget{
...
#override
Widget build(BuildContext context) {
return your MOF list
...
class TabCIBD extend StatelessWidget{
...
#override
Widget build(BuildContext context) {
return your CIBD list
then use is as a widget in your main Screen
...
body : Column(
children: [
// here will be your search bar
...
// next is your tabbar
Container(
child: TabBar(
tabs: [tab1, tab2 ]
)
Expanded(
child: TabBarView(
children:[
TabMOF(),
TabCIBD(),
]
]
with this way, your tab will not rebuild. but you have to update the List data for each tab.
if you need to rebuild your stateFullwidget tab, you can change the valuekey. article
actually its hard to explain very detail, hope you got what i mean. thank you

Related

Flutter: How to make chat page bottom text bar

I am trying to make a chat page on flutter that looks like this: top bar + bottom textField and send button. I have been able to make both the top appbar and the textfield with the send button, but I have not been able to position the textfield at the bottom of the page successfully. I have tried things like Align and Expand, footer, BottomNavigationBar.
The current version of the code I have pasted only shows the top Appbar. If I take out the child Row and the send button, I have a textfield at the bottom of the page. For some reason, as soon as I add the child Row in order to be able to add the send button, the entire textfield does not show up on the screen. I would appreciate any help.
Note: Since I am trying to make a chat screen, I want the middle section to be scrollable (while the top and bottom remain), where I can later add the chat bubbles.
Screenshot of code because of the bad editing of code snippet
Continuation of code snippet
"""
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ColorConstant.whiteA700,
// Top bar
appBar: AppBar(
backgroundColor: ColorConstant.deepOrange300,
title: Text("Match's Name",style: AppStyle.textstyleinterregular15.copyWith(
fontSize: getFontSize(15))),
),
body: Column(
children: [
Expanded(child: SingleChildScrollView(
child: Column(
children: [
// Bubbles
],
),
),
),
Container(
height: 45,
width: double.infinity,
color: ColorConstant.whiteA700,
child: Row(children: <Widget>[
TextField(
decoration: InputDecoration(
hintText: "Message...",
hintStyle: TextStyle(color: ColorConstant.bluegray100),
border: OutlineInputBorder(
borderSide: BorderSide(color: ColorConstant.bluegray100)
),
)
),
SizedBox(width: 15,),
// Send Button
FloatingActionButton(
onPressed: (){},
child: Icon(Icons.send,color: ColorConstant.whiteA700,size: 18,),
backgroundColor: ColorConstant.lightBlueA100,
elevation: 0,
),
]),
);
],
),
);
"""
Try this one...
bottomNavigationBar: Padding(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
height: 45,
width: MediaQuery.of(context).size.width,
color: Colors.white,
child: Row(
children: [
Expanded(
child: const TextField(
decoration: InputDecoration(
hintText: "Message...",
hintStyle: TextStyle(color: Colors.blue),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue)),
),
),
),
SizedBox(
width: 15,
),
// Send Button
MaterialButton(
color: Colors.red,
onPressed: () {},
child: Icon(
Icons.send,
color: Colors.white,
size: 18,
),
// backgroundColor: ColorConstant.lightBlueA100,
elevation: 0,
),
],
),
),
),
Wrap TextField with Expanded widget it'll work for you.

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 move AppBar when Keyboard is opened?

So my app looks like this:
Now, when I click the TextField in the BottomAppBar, I see this:
Code:
bottomNavigationBar: Container(
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.all(Radius.circular(20))),
padding: EdgeInsets.all(12.0),
child: TextField(
decoration: InputDecoration(hintText: 'Add a new item'),
onSubmitted: (str) => {},
)),
I want the BottomAppBar to move up when I type so I can see what I'm typing. How do I go about doing this?
I would personally suggest not using the bottomnavigationbar, which is meant to disappear under a keyboard, but instead just use a bottom-aligned view in the body of your scaffold. This will automatically move up when the keyboard appears. If you put the ListView and the Container with the textfield into a column and wrap the ListView in an Expanded widget, it should work pretty easily:
Column(
children: [
Expanded(child: ListView(...)), // your item list
Container(...), // your textfield
]
)
You should try with stack
Stack(
children: [
SingleChildScrollView(
---------
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.all(Radius.circular(20))),
padding: EdgeInsets.all(12.0),
child: TextField(
decoration: InputDecoration(hintText: 'Add a new item'),
onSubmitted: (str) => {},
)),
),
],
)
Add this to your Container in the bottomNavigationBar property:
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
Or to keep the padding you already added:
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
).add(
const EdgeInsets.all(12.0),
),
What #fravolt suggested is right in the sense that bottomnavigationbar should not move as per Material Design.
In my case I moved the widget up using the Translate widget:
Widget build(BuildContext context) {
final mediaQueryData = MediaQuery.of(context);
Transform.translate(
offset: Offset(
0.0,
-1 * mediaQueryData.viewInsets.bottom,
),
child: Container(
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.all(Radius.circular(20))),
padding: EdgeInsets.all(12.0),
child: TextField(
decoration: InputDecoration(hintText: 'Add a new item'),
onSubmitted: (str) => {},
)),
}

How do I get a black banner with text to dynamically show depending upon what a user does on another screen in 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

How to stack a widget below a TextField and make it grows to the bottom as more content loaded?

I want to make this kind of input field.
I found a flutter_chip_input package in pubdev that already serve this functionality but it was kinda buggy. I have no time to wait for the next update of the plugin so I decided to move on and build it my self.
In web app, I can easily achieve this design using relative-absolute property in css and some javascript.
Yes, I already tried using Stack widget, but the problem is the Container (a wrapper of the white list in the bottom of the input field) won't stick to the bottom.
Whenever more content is loaded, the height grows to the top instead of going to the bottom, causing the input field to be hidden below the container.
I only need to know about how to make the list stick to the bottom of the input field and make the height grows to the bottom. I'll take care the rest of it.
Any clue? Please have a look at my current script:
#override
Widget build(BuildContext context) {
return Stack(
alignment: AlignmentDirectional.bottomCenter,
overflow: Overflow.visible,
children: [
TextFormField(
focusNode: _focusNode,
decoration: widget.decoration,
onChanged: (String query) {
print(query);
},
),
Positioned(
bottom: -30,
left: 0,
right: 0,
child: Container(
height: 100,
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(20),
),
child: Text('asdasdasd'),
),
),
],
);
}
Thanks in advance!
You can use OverlayEntry to make it float and CompositeTransformTarget and CompositeTransformFollower combined with LayerLink so the list will stick to the bottom of input.
You can read this article for example implementation
Flutter: Using Overlay to display floating widgets
Try using a searchDelegate widget if you're okay with it occupying the full screen. It's quite simple to use.
Docs here: https://api.flutter.dev/flutter/material/SearchDelegate-class.html
EDIT: My bad, it is not necessary to use it in the appBar. Call
showSearch(context: context, delegate: SearchDel()); from anywhere. It takes up the full screen tho.
If not, I'll have to add the code for the widget. Thought of some ways to make it.
How About This? without Stack juga bisa kok
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IntrinsicWidth(
child: Container(
constraints: BoxConstraints(
minWidth: 200,
),
child: TextFormField(
onChanged: (String query) {
print(query);
},
),
),
),
Container(
constraints: BoxConstraints(
minHeight: 100,
),
child: Column(
children: [
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(8),
),
child: Text('asdasdasd'),
),
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(8),
),
child: Text('asdasdasd'),
),
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(8),
),
child: Text('asdasdasd'),
),
]
),
),
],
),