Reusing widgets in flutter/dart - dart

I have the following Flutter code, and I'm trying to figure out how to put section 1 into a separate class so that I can reuse it on multiple screens, and then separately (not at the same time, but instead of), how to put section 2 (which is a larger portion of code) into a separate class and how to reuse that on multiple pages with a variable to be able to change the title. Currently, I'm just copying and pasting the entire code into each screen, but I know there has to be a better way by reusing code.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
//------------------START SECTION 2---------------------------------------------
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text(
"Welcome",
style: TextStyle(color: Colors.white),
),
actions: <Widget>[
// action button
//------------------START SECTION 1---------------------------------------------
PopupMenuButton<String>(
//onSelected: showMenuSelection
//icon: new Icon(Icons.add, color: Colors.blueGrey),
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
const PopupMenuItem<String>(
value: 'Item 1', child: Text('Item 1')),
const PopupMenuItem<String>(
value: 'Item 2', child: Text('Item 2')),
],
),
//------------------END SECTION 1---------------------------------------------
],
),
//------------------END SECTION 2---------------------------------------------
body: Center(
child: Text('Hello World'),
),
),
);
}
}

VS code lets you extract the widget with a few clicks, If you're using VS code - select the line where code for widget starts. Hit Ctrl + ., select Extract widget option, enter name of your choice. And then you can customize extracted widget to take in different parameters and return widget accordingly. Same can be done with any IDE, but I am unaware of the procedure.
Edit 1: since I am unable to post screenshots right now, I found this SO answer that could help. :) https://stackoverflow.com/a/51235410/4794396

You can try this. I'm doing this way. I've created a class in which there's a function which holds my AppBar in main.dart file.
Example:
class MyAppBar {
setAppBar(context, String title) {
return new AppBar(
backgroundColor: Colors.blue,
title: Text(
title,
style: TextStyle(color: Colors.white),
),
actions: <Widget>[
// action button
//------------------START SECTION 1---------------------------------------------
PopupMenuButton<String>(
//onSelected: showMenuSelection
//icon: new Icon(Icons.add, color: Colors.blueGrey),
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
const PopupMenuItem<String>(
value: 'Item 1', child: Text('Item 1')),
const PopupMenuItem<String>(
value: 'Item 2', child: Text('Item 2')),
],
),
//------------------END SECTION 1---------------------------------------------
],
);
}
}
Usage will be, you have to import your main.dart file in the files where you want to set AppBar.
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(255, 255, 255, 20.0),
appBar: MyAppBar().setAppBar(context, 'Title for AppBar'),
body: new Container(), // your body goes here.
);
}
You can set the popup menu the same way. I'll give an example but you'll have to make it work your way.
class PopupMenuButtonBuilder {
setPopupButton() {
return <Widget>[
PopupMenuButton<String>(
//onSelected: showMenuSelection
//icon: new Icon(Icons.add, color: Colors.blueGrey),
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
const PopupMenuItem<String>(
value: 'Item 1',
child: Text(
'Item 1',
),
),
const PopupMenuItem<String>(
value: 'Item 2',
child: Text(
'Item 2',
),
),
],
),
];
}
}
Usage of above class will be:
// this `actions` is of `AppBar`.
actions: PopupMenuButtonBuilder().setPopupButton(),
If you want to have different name of the PopupMenuItem you can pass the title in setPopupButton() function.

Related

Flutter: How to display a text from Firebase?

I have searched a lot but I can't find a solution for this specific problem:
So I want to display a text in my flutter application. But this text shall be variable, so I integrated Firebase to my project. And everything is working well, so I already managed to show images from Firebase but I really don't know how to display a text.
Can you please show me how to do this? Maybe someone could show me the code I need to use to make this work?
This is my code so far, I didn't integrate the specific code to communicate with my Firebase backend, because I don't know how to do this.
import 'package:flutter/material.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
class MapsPage extends StatefulWidget {
MapsPage({Key key}) : super(key: key);
#override
_MapsPageState createState() => _MapsPageState();
}
class _MapsPageState extends State<MapsPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Firebase'),
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xffFBD23E), Color(0xffF6BE03)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
),
),
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xffFEFDFD), Color(0xffBDBDB2)],
begin: Alignment.topLeft,
end: Alignment.bottomRight),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: RichText(
text: TextSpan(
style: TextStyle(color: Colors.black),
text: 'Some text',
children: [
TextSpan(
text:
'I want this TextSpan to be variable. So if I change the data in my Firestore Database this text shall also change.',
),
TextSpan(
text: 'And some more text.',
),
],
),
),
),
],
),
),
);
}
}
Can you please help me? Thank you so much!!
Below is a screenshot of my firestore.
.
// This below returns the text
Future<Map<String, dynamic>> getData() async {
DocumentReference<Map<String, dynamic>> document =
FirebaseFirestore.instance.doc('KBADatum/6j5Fnvj0gNkSCRIx7ecH'); // path to doc
DocumentSnapshot<Map<String, dynamic>> query = await document.get();
print(query.data());
return query.data();
}
// and this is how you consume it.
FutureBuilder<Map<String, dynamic>>(
future: getData(),
builder: (BuildContext context, AsyncSnapshot<Map<String, dynamic>> snapshot) {
if (snapshot.hasError) return CircularProgressIndicator();
if (snapshot.connectionState == ConnectionState.waiting)
return CircularProgressIndicator();
return RichText(
text: TextSpan(
style: TextStyle(color: Colors.black),
text: 'Some text',
children: [
TextSpan(
text: snapshot.data['DatumJahr'], // first text
),
TextSpan(
text: 'And some more text.',
),
],
),
);
},
)
The snapshots() method provides a stream which you can subscribe to get the latest document changes. To update your ui using the stream, you can use StreamBuilder which builds itself based on the latest snapshot of interaction.
One final thing is that you can't use StreamBuilder as a child to a TextSpan. So, you will either rebuild the RichText widget or use WidgetSpan to rebuild only the span when there is an event on your stream.
Here is an example:
RichText(
text: TextSpan(
style: TextStyle(color: Colors.black),
text: 'Some text',
children: [
// Use WidgetSpan instead of TextSpan, which allows you to have a child widget
WidgetSpan(
// Use StreamBuilder to listen on the changes of your Firestore document.
child: StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection('my_collection')
.doc('my_document')
.snapshots(),
builder: (context, snapshot) {
final document = snapshot.data; // Get the document snapshot
final text = document?.data()?['my_text']; // Get the data in the text field
return Text(text ?? 'Loading...'); // Show loading if text is null
},
),
),
TextSpan(
text: 'And some more text.',
),
],
),
)
Note: I tried to keep the example simple as far as possible, but you can learn more about StreamBuilder to handle errors/data and the state of connection.

Need a persistent/same Bottom Navigation Bar for all screens - Flutter

I am a beginner with flutter and dart. I have been trying to implement a navigationBar on three different pages in my app. The toggling works well for an individual page but I have problems persisting the active and inactive tabs state on all the pages. It seems like when it navigates to another page, I lose the active state too the tabs. This is my code.
AppFooter.dart
import 'package:flutter/material.dart';
class AppFooter extends StatefulWidget {
#override
_AppFooterState createState() => _AppFooterState();
}
class _AppFooterState extends State<AppFooter> {
int index = 0;
#override
Widget build(BuildContext context) {
return new Theme(
data: Theme.of(context).copyWith(
// sets the background color of the `BottomNavigationBar`
canvasColor: Colors.white,
// sets the active color of the `BottomNavigationBar` if `Brightness` is light
primaryColor: Colors.green,
textTheme: Theme.of(context)
.textTheme
.copyWith(caption: new TextStyle(color: Colors.grey))),
child: new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: index,
onTap: (int index) {
setState(() {
this.index = index;
});
switch (index){
case 0: Navigator.of(context).pushNamed('/dashboard');
break;
case 1: Navigator.of(context).pushNamed('/medical centre');
break;
case 2: Navigator.of(context).pushNamed('/history');
break;
}
},
items: [
new BottomNavigationBarItem(
backgroundColor: Colors.white,
icon: index==0?new Image.asset('assets/images/dashboard_active.png'):new Image.asset('assets/images/dashboard_inactive.png'),
title: new Text('Dashboard', style: new TextStyle(fontSize: 12.0))),
new BottomNavigationBarItem(
backgroundColor: Colors.white,
icon: index==1?new Image.asset('assets/images/medical_sevice_active.png'):new Image.asset('assets/images/medical_sevice_inactive.png'),
title: new Text('Health Services', style: new TextStyle(fontSize: 12.0))),
new BottomNavigationBarItem(
icon: InkWell(
child: Icon(
Icons.format_align_left,
// color: green,
size: 20.0,
),
),
title: new Text('History', style: new TextStyle(fontSize: 12.0))),
]),
);
}
}
If I understand your question correctly, you need the bottom navigation bar persisted on all three pages. There is a well-written article on how to achieve it. You can find the details here.
https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf
https://github.com/bizz84/nested-navigation-demo-flutter
All credits go to the original author.
Use PageView and bottomNavigationBar:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter App';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: App(),
);
}
}
class App extends StatefulWidget {
App({Key key}) : super(key: key);
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
PageController _myPage;
var selectedPage;
#override
void initState() {
super.initState();
_myPage = PageController(initialPage: 1);
selectedPage = 1;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
physics: NeverScrollableScrollPhysics(),
controller: _myPage,
children: <Widget>[
Center(
child: Text("Another Page"),
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Page 1"),
RaisedButton(
onPressed: () {
_myPage.jumpToPage(0);
setState(() {
selectedPage = 0;
});
},
child: Text("Go to another page"),
)
],
)),
Center(child: Text("Page 2")),
Center(child: Text("Page 3")),
],
),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
icon: Icon(Icons.home),
color: selectedPage == 1 ? Colors.blue : Colors.grey,
onPressed: () {
_myPage.jumpToPage(1);
setState(() {
selectedPage = 1;
});
},
),
IconButton(
icon: Icon(Icons.star),
color: selectedPage == 2 ? Colors.blue : Colors.grey,
onPressed: () {
_myPage.jumpToPage(2);
setState(() {
selectedPage = 2;
});
},
),
IconButton(
icon: Icon(
Icons.settings,
),
color: selectedPage == 3 ? Colors.blue : Colors.grey,
onPressed: () {
_myPage.jumpToPage(3);
setState(() {
selectedPage = 3;
});
},
),
],
),
));
}
}
In addition, if you want preserve the state between pages such that going to another page won't cause the previous page to lose its state, use AutomaticKeepAliveClientMixin
Also, to lazily load the pages, PageView.builder is another solution.
Hope it helps.
Another great solution is the persistent_bottom_nav_bar package provided by Bilal Shahid.
It is easy to use and offers you a bunch of features:
Highly customizable persistent bottom navigation bar.
Ability to push new screens with or without bottom navigation bar.
20 styles for the bottom navigation bar.
Includes functions for pushing screen with or without the bottom navigation bar i.e. pushNewScreen() and pushNewScreenWithRouteSettings().
Based on flutter's Cupertino(iOS) bottom navigation bar.
Can be translucent for a particular tab.
Custom styling for the navigation bar. Click here for more information.
Handles hardware/software Android back button.
Before I found this package I followed the solution from the article #Abin mentioned in his answer. But I ran into the problem, that all screens from the navbar beeing loaded on first load of the navbar which is not that perfomant. I did not mangaed to solve this, but luckily Bilal Shahid provide a good solution with his package.
All credits to him.
Just copy & past :)
main.dart:
void main() async{
runApp(MyGrillApp());
}
class MyGrillApp extends StatelessWidget {
const MyGrillApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/mainlayout': (context) => MainLayout(),
'/page1': (context) => Page1(),
'/page2': (context) => Page2(),
'/page3': (context) => Page3(),
'/page4': (context) => Page4(),
},
initialRoute: '/mainlayout',
);
}
}
main_layout.dart:
class MainLayout extends StatefulWidget {
#override
_MainLayoutState createState() => _MainLayoutState();
}
class _MainLayoutState extends State<MainLayout> {
int _currentIndex = 0;
final _page1 = GlobalKey<NavigatorState>();
final _page2 = GlobalKey<NavigatorState>();
final _page3 = GlobalKey<NavigatorState>();
final _page4 = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.miniCenterDocked,
floatingActionButton: Padding(
padding: const EdgeInsets.all(6.0),
child: FloatingActionButton(
backgroundColor: Colors.redAccent,
child: const Icon(Icons.add, color: Colors.white),
onPressed: () {
// ToDo...
},
),
),
body: IndexedStack(
index: _currentIndex,
children: <Widget>[
Navigator(
key: _page1,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => Page1(),
),
),
Navigator(
key: _page2,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => Page2(),
),
),
Navigator(
key: _page3,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => Page3(),
),
),
Navigator(
key: _page4,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => Page4(),
),
),
],
),
bottomNavigationBar: BottomAppBar(
shape: const CircularNotchedRectangle(),
clipBehavior: Clip.antiAlias,
child: BottomNavigationBar(
backgroundColor: Colors.white,
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.redAccent,
unselectedItemColor: Colors.grey,
showSelectedLabels: false,
showUnselectedLabels: false,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.date_range), label: 'Statistics'),
BottomNavigationBarItem(icon: Icon(Icons.wallet_giftcard), label: 'Wallet'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
),
);
}
}
Details screen:
class ItemDetailsPage extends StatefulWidget {
const ItemDetailsPage({Key? key}) : super(key: key);
#override
_ItemDetailsPageState createState() => _ItemDetailsPageState();
}
class _ItemDetailsPageState extends State<ItemDetailsPage> with AutomaticKeepAliveClientMixin{
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
backgroundColor: themeColorPrimary,
title: Text('Item details',),
),
body : Container(child: Text('Hello from details'),));
}
#override
bool get wantKeepAlive => true;
}
A note about routing in my solution:
If you encounter trouble when you routing by:
Navigator.pushNamed(context, '/page3');
or by:
Navigator.of(context).pushNamed(Page3());
You can fix it using MaterialPageRoute:
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Page3(),
),
);
You can use IndexedStack to persist State when you touch/change the page
Scaffold(
body: SafeArea(
top: false,
child: IndexedStack(
//Permet de garder le state des vues même quand on change de vue
index: _currentIndex,
children: _children,
),
),
bottomNavigationBar: BottomNavigationBar( items: [ ] ),
);
I highly recommend using stack. This gives you pretty much total control over how and when you would like to show bottom app bar.
Make list of all pages you want to show using your botttomAppBar. Let's say has three icons.
final List<Widget> pages=[FirstScreen(),SecondScreen(),ThirdScreen()];
In the Build Method
Scaffold(
child: Stack(
children: <Widget>[
Navigator(
key: _navigatorKey,
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute(
builder: (BuildContext context) => pages[cur_ind],
);
},
),
],
bottomNavigationBar: BottomNavigationBar(
onTap: (int index){
setState(() {
cur_ind=index;
});
},
currentIndex: cur_ind,
fixedColor: Colors.green, //let's say
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.mail),
title: Text('Messages'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person), title: Text('Profile'))
],
),
),
),
where cur_ind is the variable used to control which page to show. And since the body is stacked, the Bottom Navigation Bar will be persistent always.
I created a small, super easy to use package that let you do that effect CustomNavigator.
And wrote a tutorial about it on Medium you can find it here.
So it goes like this
// Here's the custom scaffold widget
// It takes a normal scaffold with mandatory bottom navigation bar
// and children who are your pages
CustomScaffold(
scaffold: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: _items,
),
),
// Children are the pages that will be shown by every click
// They should placed in order such as
// `page 0` will be presented when `item 0` in the [BottomNavigationBar] clicked.
children: <Widget>[
Page('0'),
Page('1'),
Page('2'),
],
// Called when one of the [items] is tapped.
onItemTap: (index) {},
);
The cool thing about this library that it works efficiently. It creates a nested navigator (which is very unpleasant to do) and uses it for navigation in your widget tree.
And of course you can always use the default navigator from MaterialApp
If you are looking for a solution that performs well (that doesn't build the tabs/pages unnecessarily) even using IndexedStack take a look at my answer here
For anyone looking for this in the future auto_route handle this pretty much well with very little boilerplate using AutoTabsScaffold.
Widget build(context) {
return AutoTabsScaffold(
routes: const [
BooksRouter(),
AccountRouter(),
],
bottomNavigationBuilder: (_, tabsRouter) {
return BottomNavigationBar(
currentIndex: tabsRouter.activeIndex,
onTap: tabsRouter.setActiveIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.book),
label: 'Books',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_box),
label: 'Account',
),
],
);
},
);
}
I am working on a beta version of an express_app plugin, which achieve the required result.
Two days ago, I implemented an addition where you can set an ExpressHome and it can be any part of your tree, in addition to setting your routes of course. When changing the routes, everything under ExpressHome will change only and the rest will stay the same (i.e. you can have a permanent bar easily.
I will publish a more-recent version this evening, and if you would like a specific demo about your use case, let me know.
i had this issue too...after days of research i came across this package
persistent_bottom_nav_bar: ^4.0.0
it quite easy to implement.
You can use a scaffold widget to contain the whole screen then put IndexedStack widget as a Body option then use at the bottom navigation option in the scaffold widget you favorite implementation of the bottom navigation bar
Scaffold(
// here is the IndexedStack as body
body: IndexedStack(
index: this._bottomNavIndex,
children: [MangaGridView(), FavoriteManga()]),
backgroundColor: Colors.black,
bottomNavigationBar: AnimatedBottomNavigationBar(
icons: [
Icons.home_outlined,
Icons.favorite_border,
Icons.settings,
],
inactiveColor: Colors.black,
activeIndex: this._bottomNavIndex,
gapLocation: GapLocation.none,
activeColor: Theme.of(context).primaryColor,
notchSmoothness: NotchSmoothness.verySmoothEdge,
leftCornerRadius: 32,
rightCornerRadius: 32,
onTap: (index) => setState(() => this._bottomNavIndex = index),
height: 70,
splashColor: Theme.of(context).primaryColor,
splashRadius: 40.0,
splashSpeedInMilliseconds: 400,
iconSize: 34,
),
);
Navigator.of(context).pushNamed(); is for Navigation with page transition. So, in this situation, the method is not match.
You can use BottomNavigationBar with Scaffold.
example code:
class AppFooter extends StatefulWidget {
#override
_AppFooterState createState() => _AppFooterState();
}
class _AppFooterState extends State<AppFooter> {
int _currentIndex = 0;
List<Widget> _pages = [
Text("page1"),
Text("page2"),
Text("page3"),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (int index) {
setState(() {
_currentIndex = index;
});
},
items: [
new BottomNavigationBarItem(
backgroundColor: Colors.white,
icon: _currentIndex == 0
? new Image.asset('assets/images/dashboard_active.png')
: new Image.asset('assets/images/dashboard_inactive.png'),
title:
new Text('Dashboard', style: new TextStyle(fontSize: 12.0))),
new BottomNavigationBarItem(
backgroundColor: Colors.white,
icon: _currentIndex == 1
? new Image.asset('assets/images/medical_sevice_active.png')
: new Image.asset(
'assets/images/medical_sevice_inactive.png'),
title: new Text('Health Services',
style: new TextStyle(fontSize: 12.0))),
new BottomNavigationBarItem(
icon: InkWell(
child: Icon(
Icons.format_align_left,
// color: green,
size: 20.0,
),
),
title: new Text('History', style: new TextStyle(fontSize: 12.0))),
],
),
);
}
}
Just make your index variable static
like:
static int index = 0;

Flutter: how to add several filters to a List and show the new results

I started learning Flutter trying to make my first app. I don't have a developper's background, so I'm trying to learn everything by doin' it.
My app is receiving some user's data from a json file (name, surname, country, level, ...) and show the whole list of user's name and by tapping on a name a second page opens where you get all the details.
What I'd like to do now is to add a "settings page", where the user can filter, using two dropboxes, the country and/or the level.
If none of the dropboxes are selected the first page should show the whole list of persons by every country and from every level (as it does now), otherwise the list should be filtered to show only the persons from the country selected and only for the level selected.
I just need a hint about what to look for and study in order to realize it. Is my actual approach for the app ok?
Thanks alot for any kind of help.
Diego
main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';
//import pages
import './contactdetails.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'USDDN EU Judges',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'USDDN EU Judges'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<List<User>> _getUser() async {
var data = await http.get(
"https://www.disconnecteddog.com/home/json/usddneujudgesdatabase.json");
var jsonData = json.decode(data.body);
List<User> users = [];
for (var u in jsonData) {
User user = User(
u["Index"],
u["Name"],
u["Country"],
u["Level"],
u["Inthesportsince"],
u["Active"],
u["English"],
u["Email"],
u["Picture"]);
users.add(user);
}
print(users.length);
return users;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
//
IconButton(icon: new Icon(Icons.filter_list, color: Colors.white,), onPressed: null)
],
),
body: Container(
child: FutureBuilder(
future: _getUser(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(child: Text("Loading judges database...")));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(
backgroundImage:
NetworkImage(snapshot.data[index].picture),
),
title: Text(snapshot.data[index].name),
subtitle: Row(
children: <Widget>[
Text("Level: "),
Text(snapshot.data[index].level),
],
),
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) =>
DetailPage(snapshot.data[index])));
},
);
},
);
}
},
),
),
);
}
}
class User {
final int index;
final String name;
final String country;
final String level;
final String inthesportsince;
final String active;
final String english;
final String email;
final String picture;
User(this.index, this.name, this.country, this.level, this.inthesportsince,
this.active, this.english, this.email, this.picture);
}
Contactdetails.dart
import 'package:flutter/material.dart';
import 'package:usddn_judges/main.dart';
class DetailPage extends StatelessWidget {
final User user;
DetailPage(this.user);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(user.name),
),
body: Container(
//height: 120.0,
child: Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 5.0),
child: Card(
margin: EdgeInsets.all(10.0),
elevation: 2.0,
child: new Column(
children: <Widget>[
new ListTile(
leading: new Icon(
Icons.account_box,
color: Colors.blue,
size: 26.0,
),
title: new Text(
user.name,
style: new TextStyle(fontWeight: FontWeight.w400),
),
),
new Divider(color: Colors.blue),
new ListTile(
leading: new Icon(
Icons.map,
color: Colors.blue,
size: 26.0,
),
title: Row(
children: <Widget>[
new Text("Country: "),
new Text(
user.country,
style: new TextStyle(fontWeight: FontWeight.w400),
),
],
),
),
new Divider(color: Colors.blue),
new ListTile(
leading: new Icon(
Icons.multiline_chart,
color: Colors.blue,
size: 26.0,
),
title: Row(
children: <Widget>[
new Text("Level: "),
new Text(
user.level,
style: new TextStyle(fontWeight: FontWeight.w400),
),
],
),
),
new Divider(color: Colors.blue),
new ListTile(
leading: new Icon(
Icons.language,
color: Colors.blue,
size: 26.0,
),
title: Row(
children: <Widget>[
new Text("English: "),
new Text(
user.english,
style: new TextStyle(fontWeight: FontWeight.w400),
),
],
),
),
new Divider(color: Colors.blue),
new ListTile(
leading: new Icon(
Icons.flash_on,
color: Colors.blue,
size: 26.0,
),
title: Row(
children: <Widget>[
new Text("Active: "),
new Text(
user.active,
style: new TextStyle(fontWeight: FontWeight.w400),
),
],
),
),
new Divider(color: Colors.blue),
new ListTile(
leading: new Icon(
Icons.event,
color: Colors.blue,
size: 26.0,
),
title: Row(
children: <Widget>[
new Text("In the sport since: "),
new Text(
user.inthesportsince,
style: new TextStyle(fontWeight: FontWeight.w400),
),
],
),
),
],
),
),
),
),
);
}
}
Main Contact List
Details Page
I think you should look into List.where().
https://api.dartlang.org/stable/2.1.0/dart-core/Iterable/where.html
By this you can filter your users based on the values within the filter.
users.where((user) => user.country == selectedCountry);
This is just an example, null handling and a smarter where clause is probably necessary.
I hope this will help you getting started.
Create a new Screen for filters, lets name it as FilterScreen. Then, you can use any state management framework (provider, BloC etc.) to store the filters that user entered in the FilterScreen. After returning the search screen, if there is any filter entered requery the list.

Flutter Menu and Navigation

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

Re: create a dropdown button in flutter

I have used a DropDownButton in my build but i want the arrow to be displayed at the end and the dropdown items to be displayed from arrow, but in my app they are displaying from the top. i have attached the screenshots for your reference.
please can you tell me how to change this or is there any other way to simply create a drop down menu.
An example would be much appreciated.
Please excuse my code as I am new to programming and any comments or suggestions are most welcome.
Many Thanks,
Mahi.
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'dart:ui';
void main(){
runApp(new BranchSetup());
}
class BranchSetup extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new _BranchSetupState();
}
}
class _BranchSetupState extends State<BranchSetup> with
WidgetsBindingObserver {
#override
Widget build(BuildContext context){
return new MaterialApp(
theme: new ThemeData(
primaryColor: const Color(0xFF229E9C),
),
title: 'Branch Setup',
home: new Scaffold(
body: new Container(
child: new ListView(
children: <Widget>[
new Container(
margin: const EdgeInsets.all(16.0),
child: new Row(
children: <Widget>[
new Expanded(
child: new TextFormField(
decoration: new InputDecoration(
labelText: 'Branch Name',
),
),
),
],
),
),
new Container(
margin: const EdgeInsets.all(16.0),
child:
new DropdownButton<String>(
items: <String>['Mens','Womans']
.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}
).toList(),
onChanged: null,
),
),
],
),
),
),
);
}
}
This looks like a bug in Flutter. I filed an issue.
In the meantime, you can work around it by wrapping your DropdownButton in a Column.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new DemoApp()));
}
class DemoApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('DropdownButton Example')),
body: new ListView(
children: [
new Column(
children: <Widget>[
new DropdownButton<String>(
items: <String>['Foo', 'Bar'].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (_) {},
),
],
),
],
),
);
}
}
You can try out the plugin that I created : flutter_search_panel. Not a dropdown plugin, but you can display the items with the search functionality.
Use the following code for using the widget :
FlutterSearchPanel(
padding: EdgeInsets.all(10.0),
selected: 'a',
title: 'Demo Search Page',
data: ['This', 'is', 'a', 'test', 'array'],
icon: new Icon(Icons.label, color: Colors.black),
color: Colors.white,
textStyle: new TextStyle(color: Colors.black, fontWeight: FontWeight.bold, fontSize: 20.0, decorationStyle: TextDecorationStyle.dotted),
onChanged: (value) {
print(value);
},
),

Resources