How to add navigation route to Card in Flutter - flutter

In the code below, I have a method myMenu on a card. How do I navigate to another page when the card is tapped? There are going to be several of these cards which will link to its own page content. Each time I add a function to for an example it gives an error. How do I do it properly?
import 'package:flutter/material.dart';
import 'package:tarjous_app/gridview_demo.dart';
void main(List<String> args) {
runApp(
new MaterialApp(home: TarjousAle(), debugShowCheckedModeBanner: false));
}
class TarjousAle extends StatefulWidget {
#override
_TarjousAleState createState() => _TarjousAleState();
}
class _TarjousAleState extends State<TarjousAle> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: Text("Study Plan"),
backgroundColor: Colors.amber,
),
body: Container(
child: GridView.count(
crossAxisCount: 3,
children: <Widget>[
MyMenu(
title: "Records",
icon: Icons.account_balance_wallet,
shape: Colors.brown,
),
MyMenu(
title: "Academy",
icon: Icons.account_balance,
shape: Colors.grey,
),
],
),
),
);
}
}
class MyMenu extends StatelessWidget {
MyMenu({this.title, this.icon, this.shape});
final String title;
final IconData icon;
final MaterialColor shape;
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(9.0),
child: InkWell(
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => GridViewDemo()),
),
splashColor: Colors.amberAccent,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
icon,
size: 80.0,
color: shape,
),
Text(title, style: new TextStyle(fontSize: 18.0))
],
),
),
),
);
}
}
In the inkwell widget, I add a function that works for all the cards. But what I really want it for each card to navigate to its own page. E.g Records should navigate to its own records page, the same thing for Academy to academy page

You could receive the page in the constructor and then go to that page, like this:
class MyMenu extends StatelessWidget {
MyMenu({this.title, this.icon, this.shape, this.page});
final Widget page;
...
}
Then, in onTap:
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => page),
)
So now you can do this:
MyMenu(
...
page: GridViewDemo1(),
),
MyMenu(
...
page: GridViewDemo2(),
)

Note that to navigate to some page, your context must contain a Navigator instance of parent. So if you try to navigate directly from MaterialApp, you might run into issues. I will not belabour the point here since it was explained very well in this thread, but it is something to keep in mind in case you happen to run into it.
Edited to address comments:
I'd do something like this for your case. Named routes make it easy to specify which route you'd like the card to take you to, which you kind of need to do if you want the same widget to take you to different routes.
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(
new MaterialApp(
home: TarjousAle(),
debugShowCheckedModeBanner: false,
routes: {
GridViewDemo.route: (context) => GridViewDemo(),
AnotherDemo.route: (context) => AnotherDemo(),
},
),
);
}
class TarjousAle extends StatefulWidget {
#override
_TarjousAleState createState() => _TarjousAleState();
}
class _TarjousAleState extends State<TarjousAle> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: Text("Study Plan"),
backgroundColor: Colors.amber,
),
body: Container(
child: GridView.count(
crossAxisCount: 3,
children: <Widget>[
MyMenu(
title: "Records",
icon: Icons.account_balance_wallet,
shape: Colors.brown,
route: GridViewDemo.route
),
MyMenu(
title: "Academy",
icon: Icons.account_balance,
shape: Colors.grey,
route: AnotherDemo.route
),
],
),
),
);
}
}
class MyMenu extends StatelessWidget {
MyMenu({this.title, this.icon, this.shape, this.route});
final String title;
final IconData icon;
final MaterialColor shape;
final String route;
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(9.0),
child: InkWell(
onTap: () => Navigator.pushNamed(context, route),
splashColor: Colors.amberAccent,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
icon,
size: 80.0,
color: shape,
),
Text(title, style: new TextStyle(fontSize: 18.0))
],
),
),
),
);
}
}
class GridViewDemo extends StatelessWidget {
static String route = '/demo';
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.brown,
appBar: AppBar(title: Text('Grid view demo')),
body: Center(
child: Text('Grid view demo'),
),
);
}
}
class AnotherDemo extends StatelessWidget {
static String route = '/another';
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(title: Text('Another demo')),
body: Center(
child: Text('Another demo'),
),
);
}
}
You can read more about the basics of navigation in official docs, and also another docs page if you fancy the named routes.

Wrap the card with GestureDetector and you can use opnTap property.
for more details Official Documentation

Try wrapping your Card in a GestureDetector like below:
GestureDetector (
child: Card(),
onTap: () {},
),

wrap the card with InkWell widget and define your navigator.push in the onTap method.
class CardWidget extends StatelessWidget {
final Function onTapCard;
const CardWidget({Key key, #required this.onTapCard}) : super(key: key);
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(9.0),
child: InkWell(
onTap: onTapCard,
splashColor: Colors.amberAccent,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
icon,
size: 80.0,
color: shape,
),
Text(title, style: new TextStyle(fontSize: 18.0))
],
),
),
),
);
}
}
then we have our list here
class CardList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
CardWidget(
onTapCard: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => YourSecondPage()),
),
),
CardWidget(
onTapCard: Navigator.push(
context,
MaterialPageRoute(builder: (context) => YourThirdPage()),
),
),
],
);
}
}

Related

How to show next page (Stateless widget) on click only in specific Container in SplitView, not all over the page

I have TestApp, where I have SplitView with 2 horizontal Containers. By clicking button in the first container on the left(blue) I want to show new page (DetailPage widget) but not all over the page, but only in the first Container. Now it shows on the whole screen. What is a best approach to do it?
import 'package:flutter/material.dart';
import 'package:split_view/split_view.dart';
void main() {
runApp(MaterialApp(
title: 'Test',
home: TestApp(),
));
}
class TestApp extends StatelessWidget {
const TestApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SplitView(
children: [
Container(
color: Colors.blue,
child: ElevatedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => DetailPage()));
},
child: const Text('CLICK')),
),
Container(color: Colors.yellow),
],
viewMode: SplitViewMode.Horizontal,
indicator: SplitIndicator(viewMode: SplitViewMode.Horizontal),
activeIndicator: SplitIndicator(
viewMode: SplitViewMode.Horizontal,
isActive: true,
),
controller: SplitViewController(limits: [null, WeightLimit(max: 1)]),
),
);
}
}
class DetailPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('')), body: Container(color: Colors.red));
}
}
When pushing a new page you will be overriding the old one, meaning the new page will not have a spiltView, the best way to do this is by changing the widget displayed inside of the splitView like this :
import 'package:flutter/material.dart';
import 'package:split_view/split_view.dart';
void main() {
runApp(MaterialApp(
title: 'Test',
home: TestApp(),
));
}
class TestApp extends StatefulWidget { // I have already changed the widgte to stateful here
const TestApp({Key? key}) : super(key: key);
#override
_TestAppState createState() => _TestAppState();
}
class _TestAppState extends State<TestApp> {
#override
Widget build(BuildContext context) {
bool Bool;
return MaterialApp(
home: SplitView(
children: [
if (Bool == false){
Container(
color: Colors.blue,
child: ElevatedButton(
onPressed: () {
setState(() {
Bool = !Bool; // this the method for inverting the boolean, it just gives it the opposite value
});
},
child: const Text('CLICK')),
),
}
else{
DetailPage()
},
Container(color: Colors.yellow),
],
viewMode: SplitViewMode.Horizontal,
indicator: SplitIndicator(viewMode: SplitViewMode.Horizontal),
activeIndicator: SplitIndicator(
viewMode: SplitViewMode.Horizontal,
isActive: true,
),
controller: SplitViewController(limits: [null, WeightLimit(max: 1)]),
),
);
}
}
class DetailPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('')), body: Container(color: Colors.red));
}
}
Above I defined a bool called Bool, when rendering the page it checks if Bool is false, in that case it returns the blue widget, if it is true then it returns the red one, and when you click on the button it inverts the bool and updates the page.
Please note that for updating the page you have to use setState which rebuilds the widget, and to use it you have to use a stateful widget since stateless widget is static and cannot be changed.
Also I haven't tested the code because I don't have split_view package, but you should be able to copy and paste it just fine, if you get any errors please let me know.
When you use Navigator.push your routing to a new page and creating a new state. I think you should use showGeneralDialog instead.
showGeneralDialog(
context: context,
pageBuilder: (BuildContext context,
Animation<double> animation, Animation<double> pagebuilder) {
return Align(
alignment: Alignment.centerLeft,
child: Card(
child: Container(
alignment: Alignment.topLeft,
color: Colors.amber,
//show half the screen width
width: MediaQuery.of(context).size.width / 2,
child: IconButton(
icon: const Icon(Icons.cancel),
onPressed: () {
Navigator.pop(context);
}))),
);
});
try to create new Navigator within Container:
GlobalKey<NavigatorState> _navKey = GlobalKey();
home: SplitView(
children: [
Container(
child: Navigator(
key: _navKey,
onGenerateRoute: (_) => MaterialPageRoute<dynamic>(
builder: (_) {
return Container(
color: Colors.blue,
child: ElevatedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => DetailPage()));
},
child: const Text('CLICK')),
);
},
),
),),

Flutter: Increment a counter from 2 different classes with counter display on a common AppBar

I have a unique .dart page
At the beginning I would like to have a counter on a common AppBar, a "button 1" which allows to increment my counter, and another button which directs me to another class.
On this class, I would like to review the counter without it being reset to zero, it must keep the starting increment.
With the "button 2" which is in this new class, I would like to be able to continue to increment this same counter.
I have 2 problems (beginner):
the AppBar shows me an error for the counter
I can't increment from "button 1" and "button 2" which are
in a different class from the counter. So I also have an error concerning these 2 buttons.
Can you help me by giving me a maximum of explanations please because I am new to Flutter.
Thanks in advance.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class TestClass1 extends StatefulWidget {
#override
TestClass1State createState() => TestClass1State();
}
class TestClass2 extends StatefulWidget {
#override
TestClass2State createState() => TestClass2State();
}
class Counter extends StatefulWidget {
const Counter({Key? key}) : super(key: key);
#override
CounterState createState() => CounterState();
}
class AppBarShared extends StatelessWidget implements PreferredSizeWidget {
final Color backgroundColor = Colors.red;
final Text title;
final AppBar appBar;
final List<Widget> widgets;
const AppBarShared({required this.title, required this.appBar, required this.widgets});
#override
Widget build(BuildContext context) {
return AppBar(
title: title,
backgroundColor: backgroundColor,
actions: <Widget>[Counter()],
);
}
#override
Size get preferredSize => new Size.fromHeight(appBar.preferredSize.height);
}
class CounterState extends State<Counter> {
int _counter = 1;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child:Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
)
);
}
}
class TestClass1State extends State<TestClass1> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBarShared(
title: Text('title'),
appBar: AppBar(),
widgets: [this.widget],
),
//backgroundColor: Color(0xffe80fd3),
body: Container (
child: Stack(
children: [
IconButton (
iconSize: 40,
onPressed: _incrementCounter,
icon:Icon(Icons.arrow_upward, color:Colors.amber)
),
button1(),
],
),
),
);
}
Container button1() {
return Container(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton (
iconSize: 40,
onPressed: () => Navigator.pushReplacement(
context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) => TestClass2(),
transitionDuration: Duration.zero,
),
),
icon:Icon(Icons.arrow_upward, color:Colors.amber)
)
],
),
);
}
}
class TestClass2State extends State<TestClass2> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBarShared(
title: Text('title 2'),
appBar: AppBar(),
widgets: [this.widget],
),
//backgroundColor: Color(0xffe80fd3),
body: Container(
child: Stack(
children: [
IconButton (
iconSize: 40,
onPressed: _incrementCounter,
icon:Icon(Icons.arrow_upward, color:Colors.amber)
),
button2(),
],
),
),
);
}
Container button2() {
return Container(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton(
iconSize: 40,
onPressed: () => Navigator.pushReplacement(
context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) => TestClass1(),
transitionDuration: Duration.zero,
),
),
icon: Icon(Icons.arrow_upward, color: Colors.amber)
)
],
),
);
}
}

Expandable button overflowing top of container

I'm trying to make an expandable button, a bit like the expandable fab, except it's not a fab as it is not floating. This is the expandable fab for perspective:
What I'm trying to achieve though is to have a self contained button that expands above it with a menu. Self contained is in bold because I'd like the widget to be used easily without having to modify the parents structure.
So if you copy paste the code below in dartpad you'll see a yellow bar at the bottom. However if you uncomment the lines which are commented, which represents the menu expanding, you'll see that the bottom bar is pushed to the top.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: [
Expanded(child: Container(color: Colors.purple)),
MyWidget(),
]
),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SizedOverflowBox(
size: Size(double.infinity, 100),
child: Stack(
children: [
Container(color: Colors.amber, height: 100),
// Transform.translate(
// offset: Offset(0, -400),
// child: Container(color: Colors.lightBlue, height: 400, width: 80),
// ),
]
)
);
}
}
So my questions are:
How do I achieve the required result where the bottom bar does not move and a menu above it (light blue container); modifying only MyWidget and not MyApp ?
Why in the current code the bar is pushed above ?
Overlay and OverlayEntry can help to achieve this:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: [
Expanded(child: Container(color: Colors.purple)),
MyWidget(),
]
),
),
),
);
}
}
class MyWidget extends StatelessWidget {
OverlayEntry? _overlayEntry;
_hideMenu() {
_overlayEntry?.remove();
}
_showMenu(BuildContext context) {
final overlay = Overlay.of(context);
_overlayEntry = OverlayEntry(
builder: (ctx) => Stack(
children: [
GestureDetector(
onTap: () => _hideMenu(),
child: Container(color: Colors.grey.withAlpha(100)),
),
Positioned(
bottom: 100,
left: 50,
child: Container(color: Colors.pink, height: 200, width: 50,),
),
],
)
);
overlay?.insert(_overlayEntry!);
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _showMenu(context),
child: Container(color: Colors.amber, height: 100)
);
}
}
Try this, run this code in dartpad.
It contains one parent, three child which can be called using the menu buttons,
The FloatingActionButton.extended used in this code can be replaced by any custom Widget, you can give onTap methods for clicks,
I have used simple widgets, Let me know wether you were looking for something like that, or something different.
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'I am Parent'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool showButtons = false;
var index = 0;
List<Widget> childList = [Child1(), Child2(), Child3()];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: childList[index],
),
floatingActionButton: Column(
mainAxisSize: MainAxisSize.min,
children: [
Visibility(
visible: showButtons,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton.extended(
heroTag: 'btn1',
onPressed: () {
setState(() {
index = 0;
});
},
label: Text(
"Sub Btn1",
style: TextStyle(color: Colors.black),
),
elevation: 3,
backgroundColor: Colors.yellowAccent,
),
Padding(
padding: EdgeInsets.only(top: 3),
child: FloatingActionButton.extended(
heroTag: 'btn1',
onPressed: () {
setState(() {
index = 1;
});
},
label: Text(
"Sub Btn2",
style: TextStyle(color: Colors.black),
),
elevation: 3,
backgroundColor: Colors.yellowAccent,
)),
Padding(
padding: EdgeInsets.only(top: 3),
child: FloatingActionButton.extended(
heroTag: 'btn3',
onPressed: () {
setState(() {
index = 2;
});
},
label: Text(
"Sub Btn3",
style: TextStyle(color: Colors.black),
),
elevation: 3,
backgroundColor: Colors.yellowAccent,
))
],
),
),
RaisedButton(
onPressed: () {
setState(() {
showButtons = !showButtons;
});
},
child: Text("Self Contained"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
color: Colors.yellow,
),
],
) // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class Child1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("I am Child 1"),
);
}
}
class Child2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("I am Child 2"),
);
}
}
class Child3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("I am Child 3"),
);
}
}

How to make Modal Bottom Sheet elevated equally with Bottom App Bar in Flutter?

So, I am a new Flutter Developer and currently trying to make my own flutter app without any tutorial. I am confused with the elevation of the Modal Bottom Sheet and Bottom App Bar. I want both of the widgets to be elevated equally. Currently, my app behavior is like this.. The Bottom Modal Sheet just covers the Bottom App Bar and everything else. My code is something like this.
home_screen.dart (where my Bottom Modal Sheet, FAB, Bottom App Bar is)
// Packages
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:moneige/models/transaction.dart';
// UIs
import '../ui/home_screen/app_bar_title.dart';
import '../ui/home_screen/bottom_app_bar.dart';
import '../ui/home_screen/transaction_list_view.dart';
// Widgets
import '../widget/add_button.dart';
// Styles
import '../constants/styles.dart' as Styles;
class HomeScreen extends StatelessWidget {
final int totalBalance = 100000;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
title: MyAppBarTitle(totalBalance: totalBalance),
),
body: ValueListenableBuilder(
valueListenable: Hive.box('transactions').listenable(),
builder: (context, transactionBox, widget) {
return (transactionBox.length > 0)
? TransactionListView(transactionBox: transactionBox)
: Center(
child: Text('You have no transaction yet',
style: Styles.textMedium));
},
),
bottomNavigationBar: MyBottomAppBar(),
floatingActionButton: AddButton(() {
Hive.box('transactions').add(Transaction(
date: DateTime.now(),
changes: 123000,
notes: 'Crazier than usual'));
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(25.0)),
),
backgroundColor: Colors.white,
context: context,
elevation: 10,
useRootNavigator: true,
builder: (BuildContext context) {
return Container(height: 200);
});
}),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
bottom_app_bar.dart (where MyBottomAppBar is)
// Packages
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
// Widgets
import '../../widget/settings_button.dart';
import '../../widget/transaction_report_switch.dart';
// Styles
import '../../constants/styles.dart' as Styles;
class MyBottomAppBar extends StatelessWidget {
const MyBottomAppBar({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return BottomAppBar(
shape: CircularNotchedRectangle(),
color: Styles.colorPrimary,
child: Row(
children: [
Spacer(),
Container(
child: TransactionReportSwitch(() => Hive.box('transactions').deleteAll(Hive.box('transactions').keys)),
),
Container(
child: SettingsButton(),
),
],
),
);
}
}
add_button.dart (where AddButton is)
// Packages
import 'package:flutter/material.dart';
// Styles
import '../constants/styles.dart' as Styles;
class AddButton extends StatelessWidget {
final Function handler;
AddButton(this.handler);
#override
Widget build(BuildContext context) {
return FloatingActionButton(
child: Icon(
Icons.add_rounded,
color: Colors.white,
size: 28,
),
backgroundColor: Styles.colorPrimary,
focusColor: Colors.white12,
hoverColor: Colors.white12,
foregroundColor: Colors.white12,
splashColor: Colors.white24,
onPressed: handler,
);
}
}
I saw a really good animated FAB, Modal Bottom Sheet, and Bottom App Bar composition in the Flutter Gallery app, Reply example. . (https://play.google.com/store/apps/details?id=io.flutter.demo.gallery&hl=en) When the Modal Bottom Sheet appears, the FAB disappears animatedly and the Sheet and App Bar are equally elevated, also the Sheet is above the Bottom App Bar. This is the behavior I wanted in my app, do you guys have any solution?
A solution would be to have the BottomAppBar in other ascendant Scaffold in your widget tree.
Reduced example:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold( // Your Ascendant Scaffold
body: MyScreen(),
bottomNavigationBar: MyBottomAppBar() // Your BottomAppBar here
),
);
}
}
class MyScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold( // Your descendant Scaffold
body: Center(
child: Text('Hello world'),
),
);
}
}
Complete example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyWidget(),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Item 1',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Item 2',
),
],
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Builder(
builder: (context) => ElevatedButton(
child: Text('Show modal bottom sheet'),
onPressed: () => _displaysBottomSheet(context),
),
),
),
);
}
void _displaysBottomSheet(BuildContext context) {
Scaffold.of(context).showBottomSheet(
(context) => Container(
height: 200,
color: Colors.amber,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('BottomSheet'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
)
],
),
),
),
);
}
}
RESULT ON DEVICE

Alert box with multi select chip in flutter

I have a multiselect chip in my app , but when since the data in the AlertDialog depends dynamically, it will be 1 or 100, so I have added SingleChildScrollView over the alert Dialog to give scrolling if there are more entries , but when I added SingleChildScrollView my alert box goes to top of the screen like this, I want it to align in center,
If I removed the SingleChildScrollView then It will come like this which I wanted. but If there are lot of entries I cant select because It cant cover the entire data?
Is there any way where I can align it to center of screen with scroll enabled?
Thanks
showDialog(
context: context,
builder: (BuildContext context) {
return SingleChildScrollView(
child: AlertDialog(
title: Text("choose items"),
content: MultiSelectChip(
reportList,
onSelectionChanged: (selectedList) {
setState(() {
listSelectedItem = selectedList;
});
},
),
actions: <Widget>[
FlatButton(
child: Text("CANCEL"),
onPressed: () {
setState(() {
dropdownSelected = null;
listSelectedItem.clear();
});
Navigator.of(context).pop();
}),
In AlertDialog's content use Container and constraints, and in Container's child wrap SingleChildScrollView then wrap MultiSelectChip
code snippet
return AlertDialog(
title: Text("Report Video"),
content: Container(
constraints: BoxConstraints(
maxHeight: 100.0,
),
child: SingleChildScrollView(
child: MultiSelectChip(
reportList,
onSelectionChanged: (selectedList) {
setState(() {
selectedReportList = selectedList;
});
},
),
),
),
actions: <Widget>[
FlatButton(
child: Text("Report"),
onPressed: () => Navigator.of(context).pop(),
)
],
);
})
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> reportList = [
"Not relevant",
"Illegal",
"Spam",
"Offensive",
"Uncivil",
"a123",
"b234",
"c2314",
"aaaa",
"a",
"1Not relevant",
"2Illegal",
"3Spam",
"4Offensive",
"5Uncivil",
"6a123",
"7b234",
"8c2314",
"9aaaa",
"0a",
"Not relevant",
"Illegal",
"Spam",
"Offensive",
"Uncivil",
"a123",
"b234",
"c2314",
"aaaa",
"a",
"1Not relevant",
"2Illegal",
"3Spam",
"4Offensive",
"5Uncivil",
"6a123",
"7b234",
"8c2314",
"9aaaa",
"0a",
];
List<String> selectedReportList = List();
_showReportDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
//Here we will build the content of the dialog
return AlertDialog(
title: Text("Report Video"),
content: Container(
constraints: BoxConstraints(
maxHeight: 100.0,
),
child: SingleChildScrollView(
child: MultiSelectChip(
reportList,
onSelectionChanged: (selectedList) {
setState(() {
selectedReportList = selectedList;
});
},
),
),
),
actions: <Widget>[
FlatButton(
child: Text("Report"),
onPressed: () => Navigator.of(context).pop(),
)
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text("Report"),
onPressed: () => _showReportDialog(),
),
Text(selectedReportList.join(" , ")),
],
),
),
);
}
}
class MultiSelectChip extends StatefulWidget {
final List<String> reportList;
final Function(List<String>) onSelectionChanged;
MultiSelectChip(this.reportList, {this.onSelectionChanged});
#override
_MultiSelectChipState createState() => _MultiSelectChipState();
}
class _MultiSelectChipState extends State<MultiSelectChip> {
// String selectedChoice = "";
List<String> selectedChoices = List();
_buildChoiceList() {
List<Widget> choices = List();
widget.reportList.forEach((item) {
choices.add(Container(
padding: const EdgeInsets.all(2.0),
child: ChoiceChip(
label: Text(item),
selected: selectedChoices.contains(item),
onSelected: (selected) {
setState(() {
selectedChoices.contains(item)
? selectedChoices.remove(item)
: selectedChoices.add(item);
widget.onSelectionChanged(selectedChoices);
});
},
),
));
});
return choices;
}
#override
Widget build(BuildContext context) {
return Wrap(
children: _buildChoiceList(),
);
}
}