This will be a lot of explaining but i hope someone will be able to help.
Currently i have search button on my appbar that, when pressed, covers over my appbar title with a textfield
The normal appbar title is an image and i am adding functionality that when pressed, it brings you to the home screen. This is were it gets tricky, because i need to use this line of code to accomplish just that
new InkWell (
child: Image.asset(
'images/logoGrey.png',
fit: BoxFit.fill,
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
LandingPage(),
),
);
},
);
so i set that to a variable like so
class _ControlsPageState extends State<ControlsPage> {
Widget appBarTitle = new InkWell (
child: Image.asset(
'images/logoGrey.png',
fit: BoxFit.fill,
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
LandingPage(),
),
);
},
);
The reason i have this variable is so that i can change the state of the appbar(title) to a textfield when i click on the search button and back to the image when i close out.
but this wont work (error on "context") seeing as though this line of code below can only be used under "Widget build(BuildContext context)" and not in my class....
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
LandingPage(),
),
);
},
The bottom line is i need my appbar title to be a callback to the variable "appBarTitle", and the variable gets an error on "context", is there anyway i can make this work?
here is the appbar code in case it helps
appBar: AppBar(
iconTheme: new IconThemeData(color: Theme.CompanyColors.coolGrey),
backgroundColor: Colors.white,
centerTitle: true,
title: appBarTitle ,
actions: <Widget>[
new IconButton(
icon: actionIcon,
onPressed: () {
setState(() {
if (this.actionIcon.icon == Icons.search) {
this.actionIcon =
new Icon(Icons.close, color: Theme.CompanyColors.coolGrey);
this.appBarTitle = new TextField(
onSubmitted: (String str) {
setState(() {
result = str;
});
controller.text = "";
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ControlSearchPage(
search: result, title: "${widget.title}"),
),
);
},
style: new TextStyle(
color: Colors.black,
),
decoration: new InputDecoration(
prefixIcon:
new Icon(Icons.search, color: Theme.CompanyColors.coolGrey),
hintText: "Search...",
hintStyle: new TextStyle(color: Theme.CompanyColors.coolGrey)),
);
} else {
this.actionIcon =
new Icon(Icons.search, color: Theme.CompanyColors.coolGrey);
this.appBarTitle = new InkWell (
child: Image.asset(
'images/logoGrey.png',
fit: BoxFit.fill,
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
LandingPage(),
),
);
},
);
}
});
},
),
],
),
any comments will be appreciated
You should change appBarTitle to be a method that can generate the widget on state change rather than saving it to a variable. This way, you can ensure that it will only be generated when context is available.
// Define a bool to hold the current search state
bool _isSearching = false;
...
// In your build method
appBar: AppBar(
iconTheme: new IconThemeData(color: Theme.CompanyColors.coolGrey),
backgroundColor: Colors.white,
centerTitle: true,
title: _buildAppBarTitle(),
actions: <Widget>[
new IconButton(
icon: _isSearching
? new Icon(Icons.close, color: Theme.CompanyColors.coolGrey)
: new Icon(Icons.search, color: Theme.CompanyColors.coolGrey),
onPressed: () {
setState(() => _isSearching = !_isSearching);
},
),
],
),
...
// Define a separate method to build the appBarTitle
Widget _buildAppBarTitle() {
if (_isSearching) {
return new TextField(
onSubmitted: (String str) {
setState(() {
result = str;
});
controller.text = "";
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ControlSearchPage(
search: result, title: "${widget.title}"),
),
);
},
style: new TextStyle(
color: Colors.black,
),
decoration: new InputDecoration(
prefixIcon:
new Icon(Icons.search, color: Theme.CompanyColors.coolGrey),
hintText: "Search...",
hintStyle: new TextStyle(color: Theme.CompanyColors.coolGrey)),
);
} else {
return new InkWell (
child: Image.asset(
'images/logoGrey.png',
fit: BoxFit.fill,
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
LandingPage(),
),
);
},
);
}
Related
I'm using firebase to send notifications to my app what happens is that I reserved the notifications in the terminal but it's not shown in my application
so how can I handle the notification in a widget so it appears inside the application
I'm trying to set state the text message variable so that its value become the notification body instead of null but its not working
my code is that
[import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:idb/pages/aboutus.dart';
import 'package:idb/pages/adminpage.dart';
import 'package:idb/pages/changePassword.dart';
import 'package:idb/pages/homepage.dart';
import 'package:idb/pages/logout.dart';
import 'package:idb/pages/newsPage.dart';
import 'dart:io';
class NotificationsPage extends StatefulWidget {
_NotificationsPageState createState() => _NotificationsPageState();
}
class _NotificationsPageState extends State<NotificationsPage> {
final FirebaseMessaging _messaging = new FirebaseMessaging();
//String testMessage;
Map<String, dynamic> testMessage;
void firebaseCloudMessaging_Listeners() {
if (Platform.isIOS) iOS_Permission();
_messaging.getToken().then((token) {
print('notification token $token');
});
_messaging.configure(
onMessage: (Map<String, dynamic> message) async {
// print('message ${message}');
setState(() {
testMessage = message\['notification'\]\['body'\];
print('testMessage ${testMessage}');
});
},
onResume: (Map<String, dynamic> message) async {
// print('message ${message}');
//print('on resume ${message\['notification'\]\['body'\]}');
setState(() {
String testMessage = message\['notification'\]\['body'\];
print('testMessage onResume ${testMessage}');
});
},
onLaunch: (Map<String, dynamic> message) async {
// print('message ${message}');
// print('on launch $message');
setState(() {
String testMessage = message\['notification'\]\['body'\];
print('testMessage onLaunch ${testMessage}');
});
},
);
}
void iOS_Permission() {
_messaging.requestNotificationPermissions(
IosNotificationSettings(sound: true, badge: true, alert: true));
_messaging.onIosSettingsRegistered
.listen((IosNotificationSettings settings) {
print("Settings registered: $settings");
});
}
int _cIndex = 1;
void _incrementTab(index) {
setState(() {
_cIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: new Drawer(
child: new ListView(children: <Widget>\[
new Container(
child: new DrawerHeader(
child: Image.asset('assets/t.jpg'),
),
),
ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (BuildContext context) => AdminPage()),
);
},
),
ListTile(
leading: Icon(Icons.credit_card),
title: Text('My Cards'),
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (BuildContext context) => HomePage()),
);
},
),
ListTile(
leading: Icon(Icons.message),
title: Text('News'),
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (BuildContext context) => NewsPage()),
);
},
),
ListTile(
leading: Icon(Icons.info),
title: Text('About Us'),
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (BuildContext context) => AboutUs()),
);
},
),
Divider(),
ListTile(
leading: Icon(Icons.lock_outline),
title: Text('Change Password'),
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (BuildContext context) => ChangePassword()),
);
},
),
Logout(),
\]),
),
appBar: AppBar(
title: Text(
'Notifications',
style: TextStyle(color: Colors.blueGrey),
),
backgroundColor: Colors.grey\[100\],
centerTitle: true,
elevation: 0.0,
),
body: Center(
child: Container(
child: Text(
'$testMessage',
style:
new TextStyle(color: Colors.grey, fontWeight: FontWeight.w400),
),
),
),
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.orange,
currentIndex: _cIndex,
//fixedColor: Colors.grey\[100\],
type: BottomNavigationBarType.shifting,
items: \[
BottomNavigationBarItem(
icon: Icon(
Icons.home,
color: Colors.blueGrey,
),
title: Text('Home',
style: TextStyle(
color: Colors.blueGrey,
)),
),
BottomNavigationBarItem(
icon: Icon(
Icons.notifications,
color: Colors.blueGrey,
),
title: Text('Notifications',
style: TextStyle(
color: Colors.blueGrey,
)),
),
BottomNavigationBarItem(
icon: Icon(
Icons.message,
color: Colors.blueGrey,
),
title: Text('News',
style: TextStyle(
color: Colors.blueGrey,
)),
),
\],
onTap: (index) {
if (index == 0) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (BuildContext context) => AdminPage()),
);
}
if (index == 1) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (BuildContext context) => NotificationsPage()),
);
}
if (index == 2) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (BuildContext context) => NewsPage()),
);
}
_incrementTab(index);
// _incrementTab(index);
},
),
);
}
#override
void initState() {
super.initState();
firebaseCloudMessaging_Listeners();
}
}
]1]1
I want to pass data from alert dialog.Alert dialog contains textfield,so whatever the user type on the textfield that text should pass to the main page (screen).Below is the code of the alert dialog
Padding(
padding: const EdgeInsets.only(left: 42.0),
child: Align(
alignment: Alignment.topCenter,
child: RaisedButton(onPressed: (){
_showDialog();
},
),
),
Padding(
padding: const EdgeInsets.only(top: 50.0),
child: new Text('// Displays text'););
void _showDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Alert Dialog title"),
content: TextField(
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Enter the number'
),
)
,
actions: <Widget>[
// usually buttons at the bottom of the dialog
Row(
children: <Widget>[
new FlatButton(
child: new Text("Cancel"),
onPressed: () {
Navigator.of(context).pop();
},
),
new FlatButton(onPressed: (){
}, child: new Text("OK"))
],
),
],
);
},
);
}
Edit new solution:
// write this in your main page
String onMainPageText;
you can display like this in on your main page! after clicking the okey in your _showdialog method Text(onMainPageText)
change your _showDialog method with the following code.
void _showDialog() {
String dialogText;
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Alert Dialog title"),
content: TextField(
onChanged: (String textTyped) {
setState(() {
dialogText = textTyped;
});
},
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: 'Enter the number'),
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
Row(
children: <Widget>[
new FlatButton(
child: new Text("Cancel"),
onPressed: () {
setState(() {
onMainPageText = '';
});
Navigator.of(context).pop();
},
),
new FlatButton(
onPressed: () {
setState(() {
onMainPageText = dialogText;
});
Navigator.of(context).pop();
},
child: new Text("OK"))
],
),
],
);
},
);
}
Old answer:
create a global TextEditingController will handle your problem you can access the text field text with textEditingConroller.text
dont forget to define textEditingController inside your class
class YourMainPageState extends State<YourMainPage>{
TextEditingController textEditingController = new TextEditingController();
}
void _showDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Alert Dialog title"),
content: TextField(
controller: textEditingController,
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: 'Enter the number'),
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
Row(
children: <Widget>[
new FlatButton(
child: new Text("Cancel"),
onPressed: () {
Navigator.of(context).pop();
},
),
new FlatButton(onPressed: () {print(textEditingController.text);}, child: new Text("OK"))
],
),
],
);
},
);
}
You can display typed text with that code :
Padding(
padding: const EdgeInsets.only(top: 50.0),
child: new Text(texEditingController.text););
The textfield has a parameter called onChanged: you can use that to pass a function
TextField(
keyboardType: TextInputType.number,
onChange: onChange
decoration: InputDecoration(
hintText: 'Enter the number'
),
)
in your main screen use this:
void onChange(String text) {
//do stuff here with text like maybe setState
}
I have a problem regarding if statement in dart, I want the user to tap the city to go to a new screen. this code work perfectly fine
class citySec extends StatelessWidget {
Widget getListView(BuildContext context) {
var listView = ListView(
children: <Widget>[
Text(
"choose ur city:",
textDirection: TextDirection.rtl,
textAlign: TextAlign.center,
),
ListTile(
leading: Icon(Icons.location_city),
title: Text("Toronto ", textDirection: TextDirection.rtl),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TorontoUniversitySection(),
),
);
},
),
],
);
return listView;
}
#override
Widget build(BuildContext context) {
return Scaffold(body: getListView(context));
}
}
Since I have a long list of cities and the previous code will make my code very long so I had to change my code. However, I faced some errors with if statements, here is what I did so far.
import 'package:flutter/material.dart';
import 'package:rate/screens/firstScreen.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Rate',
home: Scaffold(
appBar: AppBar(
title: Text("jgfnjfnj ", textDirection: TextDirection.rtl),
),
body: ListDisplay(),
),
));
}
class ListDisplay extends StatelessWidget {
List<String> litems = ["Toronto","NewYork","London","Riyadh","Dubai","Istanbul"];
#override
Widget build (BuildContext ctxt) {
return new Scaffold(
appBar: AppBar(title: Text("Please Choose your city: ", textDirection: TextDirection.ltr,),
),
body: new ListView.builder
(
itemCount: litems.length,
itemBuilder: (BuildContext ctxt, int index) {
return new ListTile(
leading: Icon(Icons.location_city),
title: Text(litems[index], textDirection: TextDirection.rtl),
onTap: () {
// begin of all IF statements
if (litems.contains("Totonto")){
Navigator.push(
ctxt,
MaterialPageRoute(
builder: (ctxt) => TorontoUniversitySection()
),
);
}
if (litems.contains("London")){
Navigator.push(
ctxt,
MaterialPageRoute(
builder: (ctxt) => LondonUniversitySection()
),
);
}
// end of all If statements
},
);
}
)
);
}
}
for example, when I press Toronto it will take me to LondonUniversitySection()
That is because in your if statements, you check whether your list contains Toronto/London and not if currently pressed one is Toronto/London. Changing litems.contains("x") to litems[index] == "x" will do the trick. Here's edited fragment:
return new ListTile(
leading: Icon(Icons.location_city),
title: Text(litems[index], textDirection: TextDirection.rtl),
onTap: () {
if (litems[index] == "Toronto") {
Navigator.push(
ctxt,
MaterialPageRoute(builder: (ctxt) => TorontoUniversitySection()),
);
} else if (litems[index] == "London") {
Navigator.push(
ctxt,
MaterialPageRoute(builder: (ctxt) => LondonUniversitySection()));
}
},
);
Also, I recommend using a switch or else-if for that, not a bunch of ifs.
Try onTap: litems.contains("Totonto")?
Navigator.push( ctxt, MaterialPageRoute( builder: (ctxt) => TorontoUniversitySection() ), )
: null
class _RegisterBodyState extends State<RegisterBody> {
FocusNode myFocusNode = new FocusNode();
FocusNode myFocusNode2 = new FocusNode();
void initState() {
super.initState();
myFocusNode = FocusNode();
myFocusNode2 = FocusNode();
}
#override
void dispose() {
// Clean up the focus node when the Form is disposed.
myFocusNode.dispose();
myFocusNode2.dispose();
super.dispose();
}
Color color;
#override
Widget build(BuildContext context) {
return Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Register",
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 70.0,
fontWeight: FontWeight.bold,
),
),
Form(
child: Column(
children: [
TextFormField(
onTap: () {
setState(() {
color = Colors.red;
});
},
autofocus: true,
focusNode: myFocusNode,
decoration: InputDecoration(
icon: Icon(
Icons.supervised_user_circle,
size: 40.0,
),
labelText: "User Name",
labelStyle: TextStyle(
color:
myFocusNode.hasFocus ?color : Colors.yellow),
),
),
TextFormField(
focusNode: myFocusNode2,
onTap: () {
setState(() {
color = Colors.black;
});
},
autofocus: false,
decoration: InputDecoration(
icon: Icon(
Icons.supervised_user_circle,
size: 40.0,
),
labelText: "User Name",
labelStyle: TextStyle(
color: myFocusNode2.hasFocus ? color : Colors.teal,
),
),
),
],
))
],
),
),
);
}
}
I'm quite new with Flutter and I'm coming from using the Angular framework. Currently, I'm experimenting with flutter to make a desktop application using the following flutter embedding project: https://github.com/Drakirus/go-flutter-desktop-embedder.
I was wondering if someone could explain to me the best way to implement the following:
The black box represents the application as a whole.
The red box represents the custom menu.
The green box represents the content of the page.
How would I go about routing between "widgets" inside of the green area without changing the widget holding the application?
I'd love some direction please.
I am contributing Drakirus 's go-flutter plugin.
This projecd had moved to https://github.com/go-flutter-desktop
The question you ask can use package responsive_scaffold
https://pub.dev/packages/responsive_scaffold
or
you can reference this doc https://iirokrankka.com/2018/01/28/implementing-adaptive-master-detail-layouts/
Basically, there two are different layouts, see comments for detail
class _MasterDetailContainerState extends State<MasterDetailContainer> {
// Track the currently selected item here. Only used for
// tablet layouts.
Item _selectedItem;
Widget _buildMobileLayout() {
return ItemListing(
// Since we're on mobile, just push a new route for the
// item details.
itemSelectedCallback: (item) {
Navigator.push(...);
},
);
}
Widget _buildTabletLayout() {
// For tablets, return a layout that has item listing on the left
// and item details on the right.
return Row(
children: <Widget>[
Flexible(
flex: 1,
child: ItemListing(
// Instead of pushing a new route here, we update
// the currently selected item, which is a part of
// our state now.
itemSelectedCallback: (item) {
setState(() {
_selectedItem = item;
});
},
),
),
Flexible(
flex: 3,
child: ItemDetails(
// The item details just blindly accepts whichever
// item we throw in its way, just like before.
item: _selectedItem,
),
),
],
);
}
For package responsive_scaffold
on-line demo https://fluttercommunity.github.io/responsive_scaffold/#/
github https://github.com/fluttercommunity/responsive_scaffold/
more template code snippets for layout
https://github.com/fluttercommunity/responsive_scaffold/tree/dev
more pictures and demo can found here https://github.com/fluttercommunity/responsive_scaffold/tree/dev/lib/templates/3-column
code snippet 1
import 'package:flutter/material.dart';
import 'package:responsive_scaffold/responsive_scaffold.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var _scaffoldKey = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ResponsiveListScaffold.builder(
scaffoldKey: _scaffoldKey,
detailBuilder: (BuildContext context, int index, bool tablet) {
return DetailsScreen(
// appBar: AppBar(
// elevation: 0.0,
// title: Text("Details"),
// actions: [
// IconButton(
// icon: Icon(Icons.share),
// onPressed: () {},
// ),
// IconButton(
// icon: Icon(Icons.delete),
// onPressed: () {
// if (!tablet) Navigator.of(context).pop();
// },
// ),
// ],
// ),
body: Scaffold(
appBar: AppBar(
elevation: 0.0,
title: Text("Details"),
automaticallyImplyLeading: !tablet,
actions: [
IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
if (!tablet) Navigator.of(context).pop();
},
),
],
),
bottomNavigationBar: BottomAppBar(
elevation: 0.0,
child: Container(
child: IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
),
),
body: Container(
child: Center(
child: Text("Item: $index"),
),
),
),
);
},
nullItems: Center(child: CircularProgressIndicator()),
emptyItems: Center(child: Text("No Items Found")),
slivers: <Widget>[
SliverAppBar(
title: Text("App Bar"),
),
],
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: Text(index.toString()),
);
},
bottomNavigationBar: BottomAppBar(
elevation: 0.0,
child: Container(
child: IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text("Snackbar!"),
));
},
),
),
);
}
}
code snippet 2
import 'package:flutter/material.dart';
import 'package:responsive_scaffold/responsive_scaffold.dart';
class MultiColumnNavigationExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ThreeColumnNavigation(
title: Text('Mailboxes'),
showDetailsArrows: true,
backgroundColor: Colors.grey[100],
bottomAppBar: BottomAppBar(
elevation: 1,
child: Row(
children: <Widget>[
IconButton(
icon: Icon(
Icons.filter_list,
color: Colors.transparent,
),
onPressed: () {},
),
],
),
),
sections: [
MainSection(
label: Text('All Inboxes'),
icon: Icon(Icons.mail),
itemCount: 100,
itemBuilder: (context, index, selected) {
return ListTile(
leading: CircleAvatar(
child: Text(index.toString()),
),
selected: selected,
title: Text('Primary Information'),
subtitle: Text('Here are some details about the item'),
);
},
bottomAppBar: BottomAppBar(
elevation: 1,
child: Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () {},
),
],
),
),
getDetails: (context, index) {
return DetailsWidget(
title: Text('Details'),
child: Center(
child: Text(
index.toString(),
),
),
);
},
),
MainSection(
label: Text('Sent Mail'),
icon: Icon(Icons.send),
itemCount: 100,
itemBuilder: (context, index, selected) {
return ListTile(
leading: CircleAvatar(
child: Text(index.toString()),
),
selected: selected,
title: Text('Secondary Information'),
subtitle: Text('Here are some details about the item'),
);
},
getDetails: (context, index) {
return DetailsWidget(
title: Text('Details'),
actions: [
IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
],
child: Center(
child: Text(
index.toString(),
),
),
);
},
),
],
);
}
}
I'm a noob so please take anything I say with a grain of salt.
I know 2 ways to navigate through widgets and you can find them both here
https://flutter.io/docs/development/ui/navigation
I believe the main difference I can perceive is if you want to
send data to the new 'route' or not (the named route way cannot, at least that I'm aware of);
said so you can keep your main 'screen' and change the red and green widget
using the state of the widget where they are contained
example
class BlackWidget extends StatefulWidget
bla bla bla => BlackWidgetState();
class BlackWidget extend State<BlackWidget>
Widget tallWidget = GreenWidget();
Widget bigWidget = RedWidget();
return
container, column.. etc
Row(
children:[tallWidget,bigWidget
])
button onTap => tallWidget = YellowWidget();
}
GreenWidget... bla bla bla...
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => RedWidget()),
);
}
sorry for the 'bla bla', the part you need is at the bottom,
just added the 'yellow' widget to underline that you can
actually swap the 'green widget' with anything you want
Here is a material design of Expanded panel that looks like:
I'd like to make a similar one with Flutter, not sure if I've to start with something like the below code or know, and how to complete it!
new ExpansionPanelList(
children: <ExpansionPanel>[
new ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
isExpanded = true;
return new ListTile(
// leading: item.iconpic,
title: new Text(
"First",
textAlign: TextAlign.left,
style: new TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w400,
),
));
},
body: new Text("school"),
isExpanded: true,
),
new ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
isExpanded = true;
return new ListTile(
// leading: item.iconpic,
title: new Text(
"Second",
textAlign: TextAlign.left,
style: new TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w400,
),
));
},
isExpanded: false,
body: new Text("hospital"),
),
new ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
isExpanded = true;
return new ListTile(
// leading: item.iconpic,
title: new Text(
"Third",
textAlign: TextAlign.left,
style: new TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w400,
),
));
},
body: new Text("va facility"),
isExpanded: true)
]),
UPDATE
I just need to start and have the empty panels
In case if you particularly need to mimic the images you referenced from the material design. You would want to build your own custom expansion panel.
I have a simple example using AnimatedContainer to show you how to create the expanded and collapsed effects, and it is up to you to populate both the header and the body sections with what you want.
class AnimateExpanded extends StatefulWidget {
#override
_AnimateExpandedState createState() => new _AnimateExpandedState();
}
class _AnimateExpandedState extends State<AnimateExpanded> {
double _bodyHeight = 0.0;
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.grey[500],
body: new SingleChildScrollView(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Card(
child: new Container(
height: 50.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new IconButton(
icon: new Icon(Icons.keyboard_arrow_down),
onPressed: () {
setState(() {
this._bodyHeight = 300.0;
});
},
)
],
),
),
),
new Card(
child: new AnimatedContainer(
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new IconButton(
icon: new Icon(Icons.keyboard_arrow_up),
onPressed: () {
setState(() {
this._bodyHeight = 0.0;
});
},
),
],
),
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 500),
height: _bodyHeight,
// color: Colors.red,
),
),
],
),
),
);
}
}
Here's a working example (including main etc so you can just paste into a file and run)
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class ListItem {
final WidgetBuilder bodyBuilder;
final String title;
final String subtitle;
bool isExpandedInitially;
ListItem({
#required this.bodyBuilder,
#required this.title,
this.subtitle = "",
this.isExpandedInitially = false,
}) : assert(title != null),
assert(bodyBuilder != null);
ExpansionPanelHeaderBuilder get headerBuilder =>
(context, isExpanded) => new Row(children: [
new SizedBox(width: 100.0, child: new Text(title)),
new Text(subtitle)
]);
}
class ExpansionList extends StatefulWidget {
/// The items that the expansion list should display; this can change
/// over the course of the object but probably shouldn't as it won't
/// transition nicely or anything like that.
final List<ListItem> items;
ExpansionList(this.items) {
// quick check to make sure there's no duplicate titles.
assert(new Set.from(items.map((li) => li.title)).length == items.length);
}
#override
State<StatefulWidget> createState() => new ExpansionListState();
}
class ExpansionListState extends State<ExpansionList> {
Map<String, bool> expandedByTitle = new Map();
#override
Widget build(BuildContext context) {
return new ExpansionPanelList(
children: widget.items
.map(
(item) => new ExpansionPanel(
headerBuilder: item.headerBuilder,
body: new Builder(builder: item.bodyBuilder),
isExpanded:
expandedByTitle[item.title] ?? item.isExpandedInitially),
)
.toList(growable: false),
expansionCallback: (int index, bool isExpanded) {
setState(() {
expandedByTitle[widget.items[index].title] = !isExpanded;
});
},
);
}
}
void main() => runApp(
new MaterialApp(
home: new SingleChildScrollView(
child: new SafeArea(
child: new Material(
child: new ExpansionList(
[
new ListItem(
title: "Title 1",
subtitle: "Subtitle 1",
bodyBuilder: (context) => new Text("Body 1")),
new ListItem(
title: "Title 2",
subtitle: "Subtitle 2",
bodyBuilder: (context) => new Text("Body 1"),
isExpandedInitially: true)
],
),
),
),
),
),
);
If I had to guess, you're missing the parts where you pass in expanded into each expansion header, and the part where you keep track of whether each expansion header is expanded or not.
I've done it a particular way here that assumes each title is unique; you could do something similar but rely on different properties. Or you could build everything in the initState method of your ExpansionListState equivalent.
This is a full working example of pretty much the exact UI you have in the picture in your post. You can simply download the flutter gallery from the play store to see the result. They did it a different way than I did (building everything in the initState method), and it's more complicated than what I did, but would be worth understanding as well.
Hope that helps =)
You can use ExpansionTile inside ListView like this
ListView(
shrinkWrap: true,
children: <Widget>[
ExpansionTile(
backgroundColor: Colors.amber,
leading: Icon(Icons.event),
title: Text('Test1'),
children: <Widget>[
ListTile(title: Text('Title of the item')),
ListTile(
title: Text('Title of the item2'),
)
],
),
ExpansionTile(
title: Text('Test2'),
children: <Widget>[
ListTile(title: Text('Title of the item')),
ListTile(
title: Text('Title of the item2'),
)
],
)
],
)