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,
),
),
),
],
))
],
),
),
);
}
}
Related
I am building an application where I need to use Flutter maps, everything work as expected, however if I make a build through Jankins for release mode then for some reason the maps on iOS displays blank. I have another page where the same widget is opened in a full page and there it works. My thoughts are that placing google maps in SingleChildScrolView causes the issue. Below I am attaching the source code.I have replaced the SingleChildScrolView with with list view where I pass all widgets as children but the effect is the same, the map is displayed in debug mode but when building through Jankins the map is blank , but on another page the map widget is working as expected.
Any help will be greatly appreciated as I am banging my head for hours.
Regards
class DashboardPage extends StatefulWidget {
final Model _model;
DashboardPage(this._model);
#override
_DashboardPageState createState() => _DashboardPageState();
}
class _DashboardPageState extends State<DashboardPage> {
List<Dossier> _dossiers = List();
_DashboardPageState();
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(shrinkWrap: true, children: <Widget>[
_header(StringResources.dashboardTitle),
_mapWidget(),
_header(StringResources.myFiles),
_listWidgets()
],),
);
}
Widget _mapWidget() {
return Container(
height: 300,
child: DossierMap(
compassEnabled: false,
model: widget._model,
onDossierMapViewCreated: _onMapWidgetCreated,
onDossierInfoWindowTap: _selectedMarker,
),
);
}
Widget _listWidgets() {
return StreamBuilder<List<Dossier>>(
initialData: [],
stream: widget._model.dossierService
.loadDossiers(widget._model.loginService.token)
.catchError((e) {
if (widget._model.loggedIn) {
widget._model.forcedServerLogout();
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) {
return LoginPage(
model: widget._model,
errorText: "",
onLoggedInCallback: (context) => Navigator.of(context)
.pushReplacement(MaterialPageRoute(
builder: (context) => DossiersPage(widget._model))),
);
},
));
}
}).asStream(),
builder: (_context, snapshot) {
if (snapshot.hasData) {
_dossiers = snapshot.data;
return snapshot.data.length != 0
? _buildList(snapshot.data)
: _noDossierContentWidget();
} else if (snapshot.hasError) {
return _progressIndicator();
} else {
return _progressIndicator();
}
});
}
Widget _header(String text) {
return new Container(
decoration: new BoxDecoration(color: Color(ColorResources.darkGray)),
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 12, 0, 12),
child: Align(
alignment: Alignment.centerLeft,
child: Text(text,
overflow: TextOverflow.ellipsis,
style: new TextStyle(
fontWeight: FontWeight.w500,
fontSize: 17,
color: Colors.white))),
),
);
}
void _onMapWidgetCreated(dynamic controller) {
controller.setMarkers(_dossiers);
}
Widget _buildList(List<Dossier> dossierList) {
return Container(
color: Color(ColorResources.dividerColor),
height: 400,
child: ListView.separated(
padding: EdgeInsets.all(0.0),
separatorBuilder: (context, index) =>
Divider(color: Colors.grey.shade300, height: 1.5),
itemCount: dossierList != null ? dossierList.length : 0,
shrinkWrap: true,
physics: ScrollPhysics(),
itemBuilder: (BuildContext context, int index) => DossierListItem(
dossier: dossierList[index],
fromSearch: true,
widget: Icon(
Icons.keyboard_arrow_right,
size: 30,
color: Color(ColorResources.gray),
),
onListItemClickListener: () =>
onListItemClicked(dossierList[index]),
)));
}
void onListItemClicked(Dossier dossier) {
_selectedDossier(dossier);
}
Widget _progressIndicator() {
return Center(child: CircularProgressIndicator());
}
Widget _noDossierContentWidget() {
return Container(
color: Theme.of(context).cardColor,
child: ListTile(
title: Text(
StringResources.noOwnedFiles,
softWrap: true,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).primaryTextTheme.caption,
),
));
}
void _selectedMarker(DossierCluster cluster) async {
if (cluster.isCluster) {
Dossier selected = await showDialog(
context: context,
builder: (context) {
return AlertDialog(
contentPadding: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 24.0),
title: Center(child: Text(StringResources.selectFile)),
content: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
children:
dossiersFromCluster(cluster, onDossierListMarkerClick),
),
),
actions: <Widget>[
FlatButton(
child: Text(StringResources.cancel),
onPressed: () => Navigator.pop(context),
)
],
);
});
if (selected != null) _selectedDossier(selected);
} else {
_selectedDossier(cluster.getFirst);
}
}
List<Widget> dossiersFromCluster(
DossierCluster cluster, Function onDossierListMarkerClick) {
List<Widget> widgets = List();
for (int i = 0; i < cluster.size; i++) {
Dossier dossier = cluster.get(i);
widgets.add(DossierListItem(
dossier: dossier,
fromSearch: false,
widget: Container(),
onListItemClickListener: () => onDossierListMarkerClick(dossier),
));
}
return widgets;
}
void _selectedDossier(Dossier dossier) {
widget._model.setDossier(dossier);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DossierDetailsPage(widget._model)),
);
}
void _openFullSize() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => FullScreenMapPage(widget._model, _dossiers),
),
);
}
void onDossierListMarkerClick(Dossier dossier) {
widget._model.setDossier(dossier);
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => DossierDetailsPage(widget._model)));
}
}
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 was using some shared preferences to read and save lists but all I got is an error that goes as follows:
Launching lib/main.dart on iPhone 7 in debug mode...
Xcode build done. 4.1s
Tried calling: getString("t")
#0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:50:5)
#1 ShareUtils.get
package:todolist2019/ShareUtils.dart:22
<asynchronous suspension>
#2 HomeScreenState.getTaskTitle
package:todolist2019/home.dart:38
#3 _AsyncAwaitCompleter.start (dart:async/runtime/libasync_patch.dart:49:6)
#4 HomeScreenState.getTaskTitle
package:todolist2019/home.dart:35
#5 HomeScreenState.build.<anonymous closure>
package:todolist2019/home.dart:97
#6 SliverChildBuilderDelegate.build
package:flutter/…/widgets/sliver.dart:398
Here is my code for home.dart, my home screen
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import './additem.dart';
import './ShareUtils.dart';
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return HomeScreenState();
}
}
Timer timer;
DateTime now = DateTime.now();
String formattedTime = DateFormat('kk:mm').format(now);
String formattedDate = DateFormat('EEE d MMM').format(now);
class HomeScreenState extends State<HomeScreen> {
void changeTimeAndSetPref() {
setState(() {
DateTime now = DateTime.now();
formattedTime = DateFormat('kk:mm').format(now);
formattedDate = DateFormat('EEE d MMM').format(now);
});
}
Future getTaskTitle(index) async {
shareUtils = new ShareUtils();
shareUtils.Instance();
await shareUtils.get("title"[index]);
}
void initState() {
super.initState();
// Add listeners to this class
timer = Timer.periodic(Duration(seconds: 1), (Timer t) => changeTimeAndSetPref());
}
#override
Widget build(BuildContext context) {
// TODO: implement build
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]);
return Scaffold(
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => addItem()),
);
},
icon: Icon(Icons.add),
label: Text("Add Item"),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
body: Column(
children: <Widget>[
Center(
//set the correct sizes
child: Card(
child: Column(
children: <Widget>[
Text(
formattedTime,
style: TextStyle(
fontSize: 50.0,
),
),
Text(
formattedDate,
style: TextStyle(
fontSize: 35.0,
),
),
Text(
"You have impending tasks",
style: TextStyle(
fontSize: 25.0,
),
)
],
),
),
),
Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
var title = getTaskTitle(index).toString();
var detail = taskTitleList[index];
EdgeInsets.all(16.0);
return ListTile(
title: Text(
title,
style: TextStyle(fontSize: 20.0),
),
subtitle: Text(
detail,
style: TextStyle(fontSize: 15.0),
),
onTap: () {
final snackBar = SnackBar(
content: Text('Item Removed'),
duration: Duration(seconds: 1),
);
setState(() {
taskTextList.removeAt(index);
taskTitleList.removeAt(index);
Scaffold.of(context).showSnackBar(snackBar);
});
},
);
},
itemCount: taskTextList.length,
),
)
],
),
);
}
}
And here is my code for additem.dart
import 'package:flutter/material.dart';
import './home.dart';
import './ShareUtils.dart';
class addItem extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return addItemState();
}
}
ShareUtils shareUtils;
var TaskTextField;
var TaskDetailField;
var taskTextList = [];
var taskTitleList = [];
var TaskIsImportant = false;
class addItemState extends State<addItem> {
#override
Widget build(BuildContext context) {
// TODO: implement build
void saveTask (key, value) async {
await shareUtils.set(key, value);
}
return Scaffold(
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
);
setState(()async {
if (TaskIsImportant) {
taskTextList.add("❗$TaskTextField");
taskTitleList.add("$TaskDetailField");
saveTask("title", taskTextList);
} else {
taskTextList.add("$TaskTextField");
taskTitleList.add("$TaskDetailField");
saveTask("title", taskTextList);
}
});
},
label: Text("Add Task"),
icon: Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
body: Flex(
direction: Axis.vertical,
children: <Widget>[
Flexible(
child: Container(
child: Column(
children: <Widget>[
Card(
child: Column(
children: <Widget>[
Center(
child: Text(
"Welcome!",
style: TextStyle(fontSize: 50.0),
),
),
Center(
child: Text(
"Enter your task below",
style: TextStyle(fontSize: 25.0),
),
),
],
),
),
Container(
child: TextField(
decoration: InputDecoration(
hintText: "Enter title of task to be added",
hintStyle: TextStyle(fontSize: 20.0)),
onChanged: (taskTextField) {
setState(() {
TaskTextField = taskTextField;
print(TaskTextField);
});
},
),
margin: EdgeInsets.all(16.0),
),
Container(
child: TextField(
decoration: InputDecoration(
hintText: "Enter detail of task to be added",
hintStyle: TextStyle(fontSize: 20.0)),
onChanged: (taskDetailField) {
setState(() {
TaskDetailField = taskDetailField;
print(TaskDetailField);
});
},
),
margin: EdgeInsets.all(16.0),
),
CheckboxListTile(
title: Text(
"Important",
style: TextStyle(fontSize: 25.0),
),
activeColor: Colors.blue,
value: TaskIsImportant,
onChanged: (val) {
setState(() {
TaskIsImportant = !TaskIsImportant;
print(TaskIsImportant);
});
},
),
],
),
),
)
],
));
}
}
I hope someone can help me with this error. I am running on Flutter 1.2.1. Thanks in advance!
PS: I have implemented the Akio's code and I still got errors but lesser. I have also added the first 6 lines of error message.
You can add "shared_preferences: ^0.4.0" at pubspec.yaml.
And Packages get.
And you make DartFile (Ex: Filename is ShareUtils
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
class ShareUtils {
static ShareUtils _instance;
SharedPreferences ShareSave;
factory ShareUtils() => _instance ?? new ShareUtils._();
ShareUtils._();
void Instatce() async{
ShareSave = await SharedPreferences.getInstance();
}
Future<bool> set(key, value) async{
return ShareSave.setString(key, value);
}
Future<String> get(key) async{
return ShareSave.getString(key);
}
}
And main.dart
class MyApp extends StatelessWidget {
static ShareUtils shareUtils;
#override
Widget build(BuildContext context) {
ThemeData mainTheme = new ThemeData(
primaryColor : Color.fromRGBO(20, 42, 59, 1),
buttonColor: Color.fromRGBO(0, 132, 255, 1),
accentColor: Color.fromRGBO(31, 60, 83, 1)
);
shareUtils = new ShareUtils();
shareUtils.Instatce();
MaterialApp mainApp = new MaterialApp(
title: "Your app name",
theme: mainTheme,
home: new SplashPage(),
debugShowCheckedModeBanner: true,
routes: <String, WidgetBuilder>{
"HomePage": (BuildContext context) => new HomePage(),
},
);
return mainApp;
}
}
And you can Use this at anywhere
Before to use this, add import main.dart at header
GET:
Future NextPage() async {
MyApp.shareUtils.get("token").then((token) {
print(token);
if (token == null || token == "") {
Navigator.of(context).popAndPushNamed("RegisterPage");
} else {
Navigator.of(context).popAndPushNamed("HomePage");
}
});
}
SET:
void UserInfo(code, token) async{
await MyApp.shareUtils.set("token", token);
await MyApp.shareUtils.set("code", code);
await Navigator.of(context).pushNamed("HomePage");
}
I hope help you. Thank you
In your case MyApp.shareUtils.set("state", statelist.join("#"))
And get use statelist = token.split("#");
I'm having trouble passing the data that's been filled in a textformfields and selected in a dropdown menu.
I'm trying to use the Map function to pass down String values so that I can also pass down all types of values in the future (ex. int, bool, double etc.), however it's not working so I need someone to check it out.
main.dart
import 'package:flutter/material.dart';
import 'package:workoutapp/auth/auth.dart';
import 'package:workoutapp/auth/root_page.dart';
import 'package:workoutapp/inheritedWigets/auth_provider.dart';
void main(List<String> args) {
runApp(
WorkoutManager(),
);
}
class WorkoutManager extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AuthProvider(
auth: Auth(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Workout Manager',
home: RootPage(),
theme: ThemeData(
primaryColor: Colors.indigo,
primarySwatch: Colors.indigo,
accentColor: Colors.indigoAccent,
hintColor: Colors.indigo,
brightness: Brightness.dark,
),
),
);
}
}
HomePage
import 'package:flutter/material.dart';
import 'package:workoutapp/inheritedWigets/auth_provider.dart';
import './profile_account_page.dart';
import './routines_create_page.dart';
import '../objects/Routines/routines_manager.dart';
import '../tools/custom_drawer.dart';
class HomePage extends StatelessWidget {
final VoidCallback onSignedOut;
final List<Map<String, String>> routines;
HomePage({Key key, this.onSignedOut, this.routines}) : super(key: key);
void _signedOut(BuildContext context) async {
try {
var auth = AuthProvider.of(context).auth;
await auth.signOut();
onSignedOut();
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Workout Manager', style: TextStyle(color: Colors.white)),
centerTitle: false,
actions: <Widget>[
FlatButton(
child: Text('Logout'),
onPressed: () {
return _signedOut(context);
},
),
IconButton(
icon: Icon(Icons.account_box),
tooltip: 'Profile Account',
color: Colors.white,
onPressed: () {
return Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return ProfileAccountPage();
}));
},
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return RoutinesPageCreate();
}));
},
),
body: RoutinesManager(),
drawer: CustomDrawer(),
);
}
}
RoutineManager
import 'package:flutter/material.dart';
import 'package:workoutapp/objects/routines/routines.dart';
class RoutinesManager extends StatefulWidget {
final Map<String, String> startingRoutine;
RoutinesManager({this.startingRoutine});
#override
_RoutinesManagerState createState() => _RoutinesManagerState();
}
class _RoutinesManagerState extends State<RoutinesManager> {
List<Map<String, String>> _routines = [];
#override
void initState() {
if (widget.startingRoutine != null) {
_routines.add(widget.startingRoutine);
}
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: Routines(_routines),
)
],
);
}
}
RoutinesCreatePage
import 'package:flutter/material.dart';
import 'package:workoutapp/pages/home_page.dart';
class RoutinesPageCreate extends StatefulWidget {
#override
_RoutinesPageCreateState createState() => _RoutinesPageCreateState();
}
class _RoutinesPageCreateState extends State<RoutinesPageCreate> {
final formKey = GlobalKey<FormState>();
List<Map<String, String>> _routines = [];
String _routineName, _routineDescription;
var _routineNameController = TextEditingController();
var _routineDescriptionController = TextEditingController();
List<DropdownMenuItem<String>> _dropdownListBodyPartMenuItem = [];
List<String> _dropdownListBodyPart = [
'Chest',
'Back',
'Leg',
'Shoulder',
'Abs',
];
String _selectedBodyPart;
List<DropdownMenuItem<String>> _dropdownListDayOfWeekMenuItem = [];
List<String> _dropdownListDayOfWeek = [
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday',
];
String _selectedDayOfWeek;
void loadBodyPartData() {
_dropdownListBodyPartMenuItem = [];
_dropdownListBodyPartMenuItem = _dropdownListBodyPart.map((val) {
return DropdownMenuItem<String>(
child: Text(val),
value: val,
);
}).toList();
}
void loadDayOfWeekData() {
_dropdownListDayOfWeekMenuItem = [];
_dropdownListDayOfWeekMenuItem = _dropdownListDayOfWeek.map((val) {
return DropdownMenuItem<String>(
child: Text(val),
value: val,
);
}).toList();
}
final _scaffoldState = GlobalKey<ScaffoldState>();
void _showSnakBarReset() {
_scaffoldState.currentState.showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).accentColor,
content: Text('Showing SnackBar TEST'),
),
);
}
void _showSnakBarCreateWorkoutRoutine() {
_scaffoldState.currentState.showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).accentColor,
content: Text('Workout Routine has been created'),
),
);
}
void _addRoutine(Map<String, String> routine) {
setState(() {
_routines.add(routine);
});
}
#override
Widget build(BuildContext context) {
loadBodyPartData();
loadDayOfWeekData();
return Scaffold(
key: _scaffoldState,
appBar: AppBar(
title: Text('Create Routines'),
),
body: Container(
padding: EdgeInsets.all(15.0),
child: Form(
key: formKey,
child: ListView(children: buildInputs() + buildCreateButtons()),
),
),
);
}
List<Widget> buildInputs() {
TextStyle textStyle = Theme.of(context).textTheme.title;
return [
TextFormField(
controller: _routineNameController,
validator: (value) {
if (value.length > 20) {
return 'Not a valid Routine Name';
}
},
onSaved: (value) {
return _routineName = value;
},
decoration: InputDecoration(
labelStyle: textStyle,
labelText: 'Routine Name',
hintText: 'Enter the Routine Name for this day',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
))),
Padding(padding: EdgeInsets.all(7.0)),
TextFormField(
controller: _routineDescriptionController,
validator: (value) {
if (value.length > 50) {
return 'Invalid: The Description must be 50 characters or less.';
}
},
onSaved: (value) {
return _routineDescription = value;
},
decoration: InputDecoration(
labelStyle: textStyle,
labelText: 'Description',
hintText: 'Enter the description of the Routine.',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
))),
Padding(padding: const EdgeInsets.all(7.0)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DropdownButtonHideUnderline(
child: DropdownButton(
value: _selectedBodyPart,
items: _dropdownListBodyPartMenuItem,
hint: Text('Select Body Part', style: textStyle),
onChanged: (value) {
setState(() {
_selectedBodyPart = value;
});
})),
Padding(
padding: const EdgeInsets.all(1.0),
),
DropdownButtonHideUnderline(
child: DropdownButton(
value: _selectedDayOfWeek,
items: _dropdownListDayOfWeekMenuItem,
hint: Text('Select Day of Week', style: textStyle),
onChanged: (value) {
setState(() {
_selectedDayOfWeek = value;
});
},
),
),
Padding(
padding: const EdgeInsets.all(4.0),
)
],
),
];
}
List<Widget> buildCreateButtons() {
return [
Padding(
padding: const EdgeInsets.all(5.0),
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(
textColor: Theme.of(context).primaryColorDark,
color: Theme.of(context).accentColor,
child: Text('Create Workout Routine'),
onPressed: () {
if (formKey.currentState.validate()) {
_showSnakBarCreateWorkoutRoutine();
formKey.currentState.save();
_addRoutine({
'routineName': 'Chest Workout',
'description': 'Heavy',
'bodyPart': 'Chest',
'week': 'Monday',
});
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return HomePage();
}));
} else {
return null;
}
}),
),
Expanded(
child: RaisedButton(
textColor: Theme.of(context).primaryColorLight,
color: Theme.of(context).primaryColorDark,
child: Text('Reset'),
onPressed: () {
setState(() {
_showSnakBarReset();
formKey.currentState.reset();
_selectedBodyPart = null;
_selectedDayOfWeek = null;
});
},
),
),
],
),
),
];
}
}
Routines
import 'package:flutter/material.dart';
import 'package:workoutapp/objects/routines/routines_detail.dart';
class Routines extends StatelessWidget {
final List<Map<String, String>> routines;
Routines(this.routines);
Widget _buildRoutinesItem(BuildContext context, int index) {
TextStyle textStyle = Theme.of(context).textTheme.title;
return Expanded(
child: Card(
margin: EdgeInsets.all(5.0),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['routineName'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['description'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['bodyPart'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routines[index]['week'], style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
child: Text('Details'),
onPressed: () {
return Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return RoutinesDetail(
routines[index]['routineName'],
routines[index]['description'],
routines[index]['bodyPart'],
routines[index]['week']);
}));
},
)
],
),
)
],
),
),
);
}
Widget _buildRoutinesList(context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
Widget routinesCards = Container(
child: Container(
child: Center(
child: Text("No routines found, please add some.", style: textStyle),
),
),
);
if (routines.length > 0 || routines.length <= 7) {
ListView.builder(
itemBuilder: _buildRoutinesItem,
itemCount: routines.length,
);
}
return routinesCards;
}
#override
Widget build(BuildContext context) {
return _buildRoutinesList(context);
}
}
RoutineDetailPage
import 'package:flutter/material.dart';
class RoutinesDetail extends StatelessWidget {
final String routineName, description, bodyPart, week;
RoutinesDetail(this.routineName, this.description, this.bodyPart, this.week);
#override
Widget build(BuildContext context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
return Scaffold(
appBar: AppBar(
title: Text(routineName),
centerTitle: true,
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(routineName, style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(description, style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(bodyPart, style: textStyle)),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(week, style: textStyle)),
Container(
padding: EdgeInsets.all(5.0),
child: RaisedButton(
child: Text('Delete'),
onPressed: () {
Navigator.pop(context);
},
),
),
],
),
),
);
}
}
As you can see, I'm trying to separate the code into multiple files as much as possible, so it's more "readable" and make it easy for myself to make changes to the code whenever I have to in the future.
The problem is, it's quite spit up, I don't understand how to use the data and pass it down or up to the pages or widgets as there are multiple stateful and stateless widgets that are suppose to work together to make this app possible.
You'll notice on the HomePage file (StatelessWidget), I'm trying to show the Scaffold body argument with the RoutinesManager StatefulWidget, which is in a different file. At the same time in the HomePage file, I have a Scaffold floatingActionButton argument that will take you to the RoutinesCreatePage StatefulWidget to create a List of Cards (StatelessWidget) using the ListView.builder(). However, no Card gets created under the HomePage after the "Create Workout Routine" RaisedButton gets pressed in the RoutinesCreatePage and no data gets passed.
Can someone please help me out here as I am totally clueless. Also, I'm fairly a beginner regarding flutter/dart so a solution with a relatively easy to understand explanation would be very helpful.
Note: I do have other files that contribute to this app, however I don't think they're part of the problem so I left them out intentionally.
If more information is needed, please do let me know.
Thanks you!
it looks like you misunderstand what state in Flutter is. To explain in short, state is the internal status/data/... that belongs that that specific widget. StatefulWidget has state to determine if UI should be re-rendered on its own state change. External widgets never know about other widgets' states.
So it means, any state change happening inside RoutinesCreatePage widget, only that RoutinesCreatePage knows and reacts. Unless, you inform other widgets to know something has changed.
Alright, so talking about navigation, it works like a stack structure. HomePage trigger a push to RoutinesCreatePage, then to return, you need to pop, not another push.
Here a quick fix for your code, you can try.
HomePage
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
handleNewRoutine(); <--- this is to handle navigation and retrieve returning data from pop
},
),
Future handleNewRoutine() async {
var newRoutine = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => RoutinesPageCreate());
)
if (newRoutine == null) {
// nothing returns from RoutinesPageCreate widget
// so do nothing then
} else {
// add to routine list
// and trigger list re-rendering
setState(() {
this.routines.add(newRoutine);
});
}
}
RoutinesCreatePage: when clicking submit button, just populate all data from input fields, make object model and pop to return data to where this widget was pushed.
onPressed: () {
var newRoutine = .... // populate from UI to create new Routine model object.
Navigator.pop(context, newRoutine);
}
Also, take time to read the navigation guide from official Flutter documentation. It is very detailed on this part. https://flutter.io/docs/cookbook/navigation/returning-data
Some additional comments to your code:
in RoutinesCreatePage you don't need to know application level state, I mean _routines variable is unnecessary. You only need one object to store new routine to pop back to HomePage.
in Routines, this method Widget _buildRoutinesList(context) having unused ListView creation.
if (routines.length > 0 || routines.length <= 7) {
ListView.builder(
itemBuilder: _buildRoutinesItem,
itemCount: routines.length,
);
}
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(),
),
);
},
);
}