I'm trying to add "quick search buttons" to my flutter app like (in red):
I'm not sure if this is a material design component - if it is, I was hoping to identify the name so that I can e.g. search https://pub.dev/ for an existing widget.
Does anyone know what this material design component is called (if applicable)?
How can I implement it/does an existing widget exist on e.g. https://pub.dev/?
Many thanks! :)
you can use FilterChip widget for implementing "quick search buttons"
https://api.flutter.dev/flutter/material/FilterChip-class.html
or you can also create your custom widget for achieving this UI
import 'package:flutter/material.dart';
class MyFilterChip extends StatefulWidget {
const MyFilterChip({Key? key}) : super(key: key);
#override
State<MyFilterChip> createState() => _MyFilterChipState();
}
class _MyFilterChipState extends State<MyFilterChip> {
List fruits = ['apple', 'banana', 'mango', 'papaya', 'orange', 'guava'];
int selectedIndex = -1;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(15),
child: SizedBox(
height: 30,
child: ListView.separated(
itemCount: fruits.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => FilterChip(
label: Text(fruits[index]),
selected: index == selectedIndex,
onSelected: (value) {
setState(() {
selectedIndex = index;
});
},
),
separatorBuilder: (BuildContext context, int index) =>
const SizedBox(
width: 10,
),
),
),
),
],
),
);
}
}
Those are called chips, refer to this page https://material.io/components/chips. There is also a section of how to implement these in flutter.
Related
The logic I want is to show a list of categories, and when a category is selected, the category under it will get pushed down and the selected category products will be shown.
Here is a minimal code that I'm currently working with:
class CategoryPage extends StatefulWidget {
const CategoryPage({Key key}) : super(key: key);
#override
State<CategoryPage> createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
Category selectedCategory;
final List<Category> categories = [
Category(name: 'Breakfast', products: ['Cake', 'Sandwich']),
Category(name: 'Lunch', products: ['Chicken', 'Pizza']),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Minimal Example')),
body: ListView.builder(
itemCount: categories.length,
itemBuilder: (context, index) {
final category = categories[index];
return Column(
children: [
GestureDetector(
onTap: () {
setState(() {
selectedCategory = category;
});
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
margin: const EdgeInsets.only(top: 10),
alignment: Alignment.center,
width: double.infinity,
decoration: BoxDecoration(
color: selectedCategory == category
? Colors.green
: Colors.grey,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(category.name),
Icon(
selectedCategory == category
? Icons.keyboard_arrow_up
: Icons.keyboard_arrow_down,
),
],
),
),
),
if (selectedCategory == category)
ListView.builder(
shrinkWrap: true,
itemCount: category.products.length,
itemBuilder: (context, index) {
final product = category.products[index];
return Text(product);
},
)
],
);
},
),
);
}
}
And a screenshot:
Now what I want is to not use shrinkWrap as that will remove the performance gain of using ListView.builder().
Any way I can solve this
Try CustomScrollView (sliver). For huge number of data's, Sliver always the better option either for small numbers ListView.builder(shrinkWrap) works fine
I don't know about Category class you created but this will help you to list your categories with the help of DropdownFormField.
You just have to handle the selections with onChange.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Minimal Example')),
body: ListView.builder(
itemCount: categories.length,
itemBuilder: (context, index) {
final category = categories[index];
return DropdownButtonFormField(
hint: Text(category.name),
items: category.products
.map<DropdownMenuItem<String>>(
(categoryitem) => DropdownMenuItem<String>(
value: categoryitem,
child: Text(categoryitem),
))
.toList(),
onChanged: (value) {
// selections to manage
});
},
),
);
}
I'm trying to implement clickable the selected item in the Bottom navigation bar in my Flutter app. What I'm trying to achieve is when the user clicks any item in the Bottom navigation bar the selected item page contains the button which navigates to another inner page so if I want to try to click the select item in the Bottom navigation bar it shows me the same inner page, the app changes the selected tab inside the bottom navigation bar. but if I click again the select item tab it shows me the same inner page. Any help is appreciated.
Or Maybe it's clickable but the selected tab shows me the inner page only
Any help is appreciated.
My main.dart:-
import 'package:flutter/material.dart';
import 'MyPage.dart';
import 'MyPage2.dart';
import 'MyPage3.dart';
import 'package:double_back_to_close_app/double_back_to_close_app.dart';
import 'Notifications.dart';
import 'MyCustomPage.dart';
class MyApp extends StatefulWidget {
#override
_MyAppcreateState() => _MyApp();
}
class _MyApp extends State<MyApp> {
late List<Widget> _pages;
List<BottomNavigationBarItem> _items = [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: "Home",
),
BottomNavigationBarItem(
icon: Icon(Icons.messenger_rounded),
label: "Messages",
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: "Settings",
)
];
late int _selectedPage;
#override
void initState() {
super.initState();
_selectedPage = 0;
_pages = [
MyPage(
count: 1,
),
MyPage2(
count: 2,
),
MyPage3(
count: 3,
),
];
}
#override
Widget build(BuildContext context) {
print(_selectedPage);
return Scaffold(
body: _pages[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
items: _items,
currentIndex: _selectedPage,
onTap: (index) {
setState(() {
_selectedPage = index;
});
},
)
);
}
}
MyPage.dart
import 'package:flutter/material.dart';
import 'MyCustomPage.dart';
import 'Notifications.dart';
class MyPage extends StatefulWidget {
final count;
MyPage({Key? key, this.count}) : super(key: key);
#override
_MyPage createState() => _MyPage();
}
class _MyPage extends State<MyPage>{
late int _selectedPage;
#override
Widget build(BuildContext context) {
return Navigator(
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('page 01'),
),
body: Center(
child: RaisedButton(
child: Text('my page1'),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (ctx) => MyCustomPage()
)
);
},
),
),
);
},
);
},
);
}
}
MyCustomPage.dart
import 'package:flutter/material.dart';
class MyCustomPage extends StatefulWidget {
MyCustomPage({Key? key}) : super(key: key);
#override
_MyCustomPage createState() => _MyCustomPage();
}
class _MyCustomPage extends State<MyCustomPage>{
#override
Widget build(BuildContext parentContext) {
return Scaffold(
appBar: AppBar(
title: Text('custompage'),
),
body: Column(
children: [
Expanded(
child: Container(
child: ListView.builder(
itemCount: 15,
itemBuilder: (context, index) {
return Container(
width: double.infinity,
child: Card(
child: Center(
child: Text('My Custom Page'),
),
),
);
},
),
),
),
],
),
);
}
}
I add the image for a better understanding:-
my home page view
MycustomPage/inner page view
this is my issue what I want is when I navigate to the inner page the select tab must be unselectable and when I click on that selected tab it will show the first page which is MyPage.dart page, not the inner page(MyCustomPage.dart).
Please answer me if any further questions.
Please comment to me if you don't understand it.
But please don't ignore this. I really want to do that task.
Any help is appreciated.
Till I understood this thing is not possible with default BottomNavigationBar
maybe you can with custom. this is already a predefined index in the BottomNavigationBar constructor and set it to "0"
int currentIndex = 0,
BottomNavigationBar(
{Key? key,
required List<BottomNavigationBarItem> items,
ValueChanged<int>? onTap,
int currentIndex = 0,
double? elevation,
BottomNavigationBarType? type,
Color? fixedColor,
Color? backgroundColor,
double iconSize = 24.0,
Color? selectedItemColor,
Color? unselectedItemColor,
IconThemeData? selectedIconTheme,
IconThemeData? unselectedIconTheme,
double selectedFontSize = 14.0,
double unselectedFontSize = 12.0,
TextStyle? selectedLabelStyle,
TextStyle? unselectedLabelStyle,
bool? showSelectedLabels,
bool? showUnselectedLabels,
MouseCursor? mouseCursor,
bool? enableFeedback,
BottomNavigationBarLandscapeLayout? landscapeLayout}
)
BottomNavigationBar ref
I am building a quiz app which has a homepage that allows you to select the subject you want to be quizzed on and then displays the questions. However, when I run my code, it doesn't display this homepage and instead displays the questions that should come if the last button was pressed. Here are the relevant snippets of my code:
[main.dart]
import 'package:flutter/material.dart';
import './page.dart';
import './s_button.dart';
class _MyAppState extends State<MyApp> {
List subjects = ["biology", "chemistry", "physics"];
bool PageIndex = true;
String selected_subject = "";
void changePage(s) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
selected_subject = s;
PageIndex = false;
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.pink[100],
body: PageIndex ? Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Quiz App", style: TextStyle(fontSize: 24)),
SizedBox(height: 30),
Text("Select a subject"),
SizedBox(height: 40),
...subjects.map((sub){
return SubjectButton(pageHandler: changePage, subject: sub);
})
]
),
)
: QuizPage(selected_subject)
)
);
}
}
[s_button.dart]
import 'package:flutter/material.dart';
class SubjectButton extends StatelessWidget {
final Function pageHandler;
final String subject;
const SubjectButton({Key? key, required this.pageHandler, required this.subject}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
width: 120,
height: 60,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.pink,
elevation: 5,
),
onPressed: pageHandler(subject),
child: Text(subject)
)
),
SizedBox(height: 20)
],
);
}
}
When I run this however, QuizPage() is displayed with the question for physics, which is the last button as per my initial list. Somehow, my PageIndex is being set to false and my selected_subject is being set to "physics" before I even have a chance to click on the buttons. What is going wrong?
onPressed: pageHandler(subject) means, while building the widget, it will be called.
To call on runtime use
onPressed:()=> pageHandler(subject),
use
onPressed:()=> pageHandler(subject),
instead of
onPressed: pageHandler(subject),
Check the onPressed line, you're directly executing the function and assigning the return to the onPressed but not binding it.
onPressed: pageHandler(subject),
You may meant to do the following:
onPressed: () => pageHandler(subject),
Like this, it won't get automatically executed at first :)
This panel includes a title and a listview. The listview can be scrolled. When the listview scrolls to the top, continue to slide down the list, and the entire panel will follow.
like this:
this panel include title (add event)and listview, now listview be scrolled。
Now, the list slides to the top, continuing to drop down the list, we can see the panel slide down
You can use draggableScrollableSheet in a stack to get this behaviour
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('DraggableScrollableSheet'),
),
body: SizedBox.expand(
child: DraggableScrollableSheet(
builder: (BuildContext context, ScrollController scrollController) {
return Container(
color: Colors.blue[100],
child: ListView.builder(
controller: scrollController,
itemCount: 25,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
);
},
),
),
);
}
}
More about draggableScrollableSheet
I am writing a flutter program where the user should select a value from a DropdownButtonFormField. once the selection is made, the choice should be displayed on the dropdown. I use a push route to get the data from a second screen in which the choice is utilized. My problem is after selecting the option, the page refreshes and therefore doesnt show the selected value on the dropdown.
Below is my code:
I create the Dropdownbuttonformfield in a file called shared.dart so I can call it in multiple files:
class UserDropdownList extends StatefulWidget {
#override
_UserDropdownListState createState() => _UserDropdownListState();
}
class _UserDropdownListState extends State<UserDropdownList> {
String currentUser;
#override
Widget build(BuildContext context) {
final user = Provider.of<List<User>>(context) ?? [];
return DropdownButtonFormField(
isExpanded: true,
decoration: textInputDecoration,
value: currentUser,
hint: Text(
'Incoming Officer',
),
onChanged: (val) {
setState(() => currentUser = val);
var route = MaterialPageRoute(
builder: (BuildContext context) =>
FinalForm(chosenUser: currentUser,)
);
Navigator.of(context).push(route);
},
// onChanged: (val) => setState(() => currentUser = val),
items: user.map((user){
return DropdownMenuItem(
value: user.userId,
child: Text(user.name)
);
}).toList(),
);
}
}
I then call the Custom button in my main page like so
class FinalForm extends StatefulWidget {
//code for importing selected user
final String chosenUser;
FinalForm({Key key, this.chosenUser}) : super (key: key);
#override
_FinalForm createState() => _FinalFormState();
}
class _FinalFormState extends State<FinalForm> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Final Form')
),
body: Form(
child: Center(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.fromLTRB(5, 5, 5, 5),
children: <Widget>[
SizedBox(height: 20.0),
Align(
child: Text(
'Select Incoming Officer',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.blueAccent,
),
)
),
SizedBox(height: 20.0),
StreamProvider<List<User>>.value(
value: DatabaseService().users,
child: UserDropdownList(),
),
SizedBox(height: 20.0),
Text("${widget.chosenUser}"),
],),
),
),
);
}
}
Is there a way to keep the selected value on the dropdown or prevent the screen from reloading?
If you are navigating away from the current page / view, it would make sense for the current dropdown selection to be lost. You can pass the current selection as an argument to the push function to redisplay on the new page. Hth