Lots!
New navigation system, new 'about/settings/logout' page, some more types of dialog boxes added, some more minor things
This commit is contained in:
parent
2d0b6230ae
commit
60873a07ef
13 changed files with 1142 additions and 279 deletions
|
@ -6,13 +6,13 @@ PODS:
|
|||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- Flutter (from `.symlinks/flutter/ios-profile`)
|
||||
- Flutter (from `.symlinks/flutter/ios`)
|
||||
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
|
||||
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Flutter:
|
||||
:path: ".symlinks/flutter/ios-profile"
|
||||
:path: ".symlinks/flutter/ios"
|
||||
shared_preferences:
|
||||
:path: ".symlinks/plugins/shared_preferences/ios"
|
||||
url_launcher:
|
||||
|
|
|
@ -281,7 +281,7 @@
|
|||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
|
||||
"${PODS_ROOT}/../.symlinks/flutter/ios-profile/Flutter.framework",
|
||||
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
|
|
|
@ -38,6 +38,9 @@ Future<LoginModel> requestLoginAPI(
|
|||
|
||||
return LoginModel.fromJson(responseJson);
|
||||
} else {
|
||||
debugPrint("Invalid, either creds are wrong or server is down");
|
||||
Navigator.of(context).pushReplacementNamed('/HomePage'); // just here temporarily while server is down
|
||||
|
||||
final responseJson = json.decode(response.body);
|
||||
|
||||
saveCurrentLogin(responseJson, body["email"]);
|
||||
|
|
509
lib/common/functions/customAbout.dart
Normal file
509
lib/common/functions/customAbout.dart
Normal file
|
@ -0,0 +1,509 @@
|
|||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
//import 'dart:developer' show Timeline, Flow;
|
||||
import 'dart:developer' as dev;
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/widgets.dart' hide Flow;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// A [ListTile] that shows an about box.
|
||||
///
|
||||
/// This widget is often added to an app's [Drawer]. When tapped it shows
|
||||
/// an about box dialog with [showAboutDialog].
|
||||
///
|
||||
/// The about box will include a button that shows licenses for software used by
|
||||
/// the application. The licenses shown are those returned by the
|
||||
/// [LicenseRegistry] API, which can be used to add more licenses to the list.
|
||||
///
|
||||
/// If your application does not have a [Drawer], you should provide an
|
||||
/// affordance to call [showAboutDialog] or (at least) [showLicensePage].
|
||||
class AboutListTile extends StatelessWidget {
|
||||
/// Creates a list tile for showing an about box.
|
||||
///
|
||||
/// The arguments are all optional. The application name, if omitted, will be
|
||||
/// derived from the nearest [Title] widget. The version, icon, and legalese
|
||||
/// values default to the empty string.
|
||||
const AboutListTile({
|
||||
Key key,
|
||||
this.icon = const Icon(null),
|
||||
this.child,
|
||||
this.applicationName,
|
||||
this.applicationVersion,
|
||||
this.applicationIcon,
|
||||
this.applicationLegalese,
|
||||
this.aboutBoxChildren,
|
||||
}) : super(key: key);
|
||||
|
||||
/// The icon to show for this drawer item.
|
||||
///
|
||||
/// By default no icon is shown.
|
||||
///
|
||||
/// This is not necessarily the same as the image shown in the dialog box
|
||||
/// itself; which is controlled by the [applicationIcon] property.
|
||||
final Widget icon;
|
||||
|
||||
/// The label to show on this drawer item.
|
||||
///
|
||||
/// Defaults to a text widget that says "About Foo" where "Foo" is the
|
||||
/// application name specified by [applicationName].
|
||||
final Widget child;
|
||||
|
||||
/// The name of the application.
|
||||
///
|
||||
/// This string is used in the default label for this drawer item (see
|
||||
/// [child]) and as the caption of the [AboutDialog] that is shown.
|
||||
///
|
||||
/// Defaults to the value of [Title.title], if a [Title] widget can be found.
|
||||
/// Otherwise, defaults to [Platform.resolvedExecutable].
|
||||
final String applicationName;
|
||||
|
||||
/// The version of this build of the application.
|
||||
///
|
||||
/// This string is shown under the application name in the [AboutDialog].
|
||||
///
|
||||
/// Defaults to the empty string.
|
||||
final String applicationVersion;
|
||||
|
||||
/// The icon to show next to the application name in the [AboutDialog].
|
||||
///
|
||||
/// By default no icon is shown.
|
||||
///
|
||||
/// Typically this will be an [ImageIcon] widget. It should honor the
|
||||
/// [IconTheme]'s [IconThemeData.size].
|
||||
///
|
||||
/// This is not necessarily the same as the icon shown on the drawer item
|
||||
/// itself, which is controlled by the [icon] property.
|
||||
final Widget applicationIcon;
|
||||
|
||||
/// A string to show in small print in the [AboutDialog].
|
||||
///
|
||||
/// Typically this is a copyright notice.
|
||||
///
|
||||
/// Defaults to the empty string.
|
||||
final String applicationLegalese;
|
||||
|
||||
/// Widgets to add to the [AboutDialog] after the name, version, and legalese.
|
||||
///
|
||||
/// This could include a link to a Web site, some descriptive text, credits,
|
||||
/// or other information to show in the about box.
|
||||
///
|
||||
/// Defaults to nothing.
|
||||
final List<Widget> aboutBoxChildren;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(debugCheckHasMaterial(context));
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
return ListTile(
|
||||
leading: icon,
|
||||
title: child ??
|
||||
Text(MaterialLocalizations.of(context).aboutListTileTitle(applicationName ?? _defaultApplicationName(context))),
|
||||
onTap: () {
|
||||
showAboutDialog(
|
||||
context: context,
|
||||
applicationName: applicationName,
|
||||
applicationVersion: applicationVersion,
|
||||
applicationIcon: applicationIcon,
|
||||
applicationLegalese: applicationLegalese,
|
||||
children: aboutBoxChildren,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Displays an [AboutDialog], which describes the application and provides a
|
||||
/// button to show licenses for software used by the application.
|
||||
///
|
||||
/// The arguments correspond to the properties on [AboutDialog].
|
||||
///
|
||||
/// If the application has a [Drawer], consider using [AboutListTile] instead
|
||||
/// of calling this directly.
|
||||
///
|
||||
/// If you do not need an about box in your application, you should at least
|
||||
/// provide an affordance to call [showLicensePage].
|
||||
///
|
||||
/// The licenses shown on the [LicensePage] are those returned by the
|
||||
/// [LicenseRegistry] API, which can be used to add more licenses to the list.
|
||||
///
|
||||
/// The `context` argument is passed to [showDialog], the documentation for
|
||||
/// which discusses how it is used.
|
||||
void showAboutDialog({
|
||||
@required BuildContext context,
|
||||
String applicationName,
|
||||
String applicationVersion,
|
||||
Widget applicationIcon,
|
||||
String applicationLegalese,
|
||||
List<Widget> children,
|
||||
}) {
|
||||
assert(context != null);
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AboutDialog(
|
||||
applicationName: applicationName,
|
||||
applicationVersion: applicationVersion,
|
||||
applicationIcon: applicationIcon,
|
||||
applicationLegalese: applicationLegalese,
|
||||
children: children,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Displays a [LicensePage], which shows licenses for software used by the
|
||||
/// application.
|
||||
///
|
||||
/// The arguments correspond to the properties on [LicensePage].
|
||||
///
|
||||
/// If the application has a [Drawer], consider using [AboutListTile] instead
|
||||
/// of calling this directly.
|
||||
///
|
||||
/// The [AboutDialog] shown by [showAboutDialog] includes a button that calls
|
||||
/// [showLicensePage].
|
||||
///
|
||||
/// The licenses shown on the [LicensePage] are those returned by the
|
||||
/// [LicenseRegistry] API, which can be used to add more licenses to the list.
|
||||
void showLicensePage({
|
||||
@required BuildContext context,
|
||||
String applicationName,
|
||||
String applicationVersion,
|
||||
Widget applicationIcon,
|
||||
String applicationLegalese,
|
||||
}) {
|
||||
assert(context != null);
|
||||
Navigator.push(context, MaterialPageRoute<void>(
|
||||
builder: (BuildContext context) => LicensePage(
|
||||
applicationName: applicationName,
|
||||
applicationVersion: applicationVersion,
|
||||
applicationLegalese: applicationLegalese,
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/// An about box. This is a dialog box with the application's icon, name,
|
||||
/// version number, and copyright, plus a button to show licenses for software
|
||||
/// used by the application.
|
||||
///
|
||||
/// To show an [AboutDialog], use [showAboutDialog].
|
||||
///
|
||||
/// If the application has a [Drawer], the [AboutListTile] widget can make the
|
||||
/// process of showing an about dialog simpler.
|
||||
///
|
||||
/// The [AboutDialog] shown by [showAboutDialog] includes a button that calls
|
||||
/// [showLicensePage].
|
||||
///
|
||||
/// The licenses shown on the [LicensePage] are those returned by the
|
||||
/// [LicenseRegistry] API, which can be used to add more licenses to the list.
|
||||
class AboutDialog extends StatelessWidget {
|
||||
/// Creates an about box.
|
||||
///
|
||||
/// The arguments are all optional. The application name, if omitted, will be
|
||||
/// derived from the nearest [Title] widget. The version, icon, and legalese
|
||||
/// values default to the empty string.
|
||||
const AboutDialog({
|
||||
Key key,
|
||||
this.applicationName,
|
||||
this.applicationVersion,
|
||||
this.applicationIcon,
|
||||
this.applicationLegalese,
|
||||
this.children,
|
||||
}) : super(key: key);
|
||||
|
||||
/// The name of the application.
|
||||
///
|
||||
/// Defaults to the value of [Title.title], if a [Title] widget can be found.
|
||||
/// Otherwise, defaults to [Platform.resolvedExecutable].
|
||||
final String applicationName;
|
||||
|
||||
/// The version of this build of the application.
|
||||
///
|
||||
/// This string is shown under the application name.
|
||||
///
|
||||
/// Defaults to the empty string.
|
||||
final String applicationVersion;
|
||||
|
||||
/// The icon to show next to the application name.
|
||||
///
|
||||
/// By default no icon is shown.
|
||||
///
|
||||
/// Typically this will be an [ImageIcon] widget. It should honor the
|
||||
/// [IconTheme]'s [IconThemeData.size].
|
||||
final Widget applicationIcon;
|
||||
|
||||
/// A string to show in small print.
|
||||
///
|
||||
/// Typically this is a copyright notice.
|
||||
///
|
||||
/// Defaults to the empty string.
|
||||
final String applicationLegalese;
|
||||
|
||||
/// Widgets to add to the dialog box after the name, version, and legalese.
|
||||
///
|
||||
/// This could include a link to a Web site, some descriptive text, credits,
|
||||
/// or other information to show in the about box.
|
||||
///
|
||||
/// Defaults to nothing.
|
||||
final List<Widget> children;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
final String name = applicationName ?? _defaultApplicationName(context);
|
||||
final String version = applicationVersion ?? _defaultApplicationVersion(context);
|
||||
final Widget icon = applicationIcon ?? _defaultApplicationIcon(context);
|
||||
List<Widget> body = <Widget>[];
|
||||
if (icon != null)
|
||||
body.add(IconTheme(data: const IconThemeData(size: 45.0), child: icon));
|
||||
body.add(Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: ListBody(
|
||||
children: <Widget>[
|
||||
Text(name, style: Theme.of(context).textTheme.title),
|
||||
Text(version, style: Theme.of(context).textTheme.body1),
|
||||
Text(applicationLegalese ?? '', style: Theme.of(context).textTheme.caption),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
body = <Widget>[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: body,
|
||||
),
|
||||
];
|
||||
if (children != null)
|
||||
body.addAll(children);
|
||||
return AlertDialog(
|
||||
content: SingleChildScrollView(
|
||||
child: ListBody(children: body),
|
||||
),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: Text(MaterialLocalizations.of(context).viewLicensesButtonLabel),
|
||||
onPressed: () {
|
||||
showLicensePage(
|
||||
context: context,
|
||||
applicationName: applicationName,
|
||||
applicationVersion: applicationVersion,
|
||||
applicationIcon: applicationIcon,
|
||||
applicationLegalese: applicationLegalese,
|
||||
);
|
||||
},
|
||||
),
|
||||
FlatButton(
|
||||
child: Text(MaterialLocalizations.of(context).closeButtonLabel),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A page that shows licenses for software used by the application.
|
||||
///
|
||||
/// To show a [LicensePage], use [showLicensePage].
|
||||
///
|
||||
/// The [AboutDialog] shown by [showAboutDialog] and [AboutListTile] includes
|
||||
/// a button that calls [showLicensePage].
|
||||
///
|
||||
/// The licenses shown on the [LicensePage] are those returned by the
|
||||
/// [LicenseRegistry] API, which can be used to add more licenses to the list.
|
||||
class LicensePage extends StatefulWidget {
|
||||
/// Creates a page that shows licenses for software used by the application.
|
||||
///
|
||||
/// The arguments are all optional. The application name, if omitted, will be
|
||||
/// derived from the nearest [Title] widget. The version and legalese values
|
||||
/// default to the empty string.
|
||||
///
|
||||
/// The licenses shown on the [LicensePage] are those returned by the
|
||||
/// [LicenseRegistry] API, which can be used to add more licenses to the list.
|
||||
const LicensePage({
|
||||
Key key,
|
||||
this.applicationName,
|
||||
this.applicationVersion,
|
||||
this.applicationLegalese,
|
||||
}) : super(key: key);
|
||||
|
||||
/// The name of the application.
|
||||
///
|
||||
/// Defaults to the value of [Title.title], if a [Title] widget can be found.
|
||||
/// Otherwise, defaults to [Platform.resolvedExecutable].
|
||||
final String applicationName;
|
||||
|
||||
/// The version of this build of the application.
|
||||
///
|
||||
/// This string is shown under the application name.
|
||||
///
|
||||
/// Defaults to the empty string.
|
||||
final String applicationVersion;
|
||||
|
||||
/// A string to show in small print.
|
||||
///
|
||||
/// Typically this is a copyright notice.
|
||||
///
|
||||
/// Defaults to the empty string.
|
||||
final String applicationLegalese;
|
||||
|
||||
@override
|
||||
_LicensePageState createState() => _LicensePageState();
|
||||
}
|
||||
|
||||
class _LicensePageState extends State<LicensePage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initLicenses();
|
||||
}
|
||||
|
||||
final List<Widget> _licenses = <Widget>[];
|
||||
bool _loaded = false;
|
||||
|
||||
Future<void> _initLicenses() async {
|
||||
int debugFlowId = -1;
|
||||
assert(() {
|
||||
final dev.Flow flow = dev.Flow.begin();
|
||||
dev.Timeline.timeSync('_initLicenses()', () { }, flow: flow);
|
||||
debugFlowId = flow.id;
|
||||
return true;
|
||||
}());
|
||||
await for (LicenseEntry license in LicenseRegistry.licenses) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
assert(() {
|
||||
dev.Timeline.timeSync('_initLicenses()', () { }, flow: dev.Flow.step(debugFlowId));
|
||||
return true;
|
||||
}());
|
||||
final List<LicenseParagraph> paragraphs =
|
||||
await SchedulerBinding.instance.scheduleTask<List<LicenseParagraph>>(
|
||||
license.paragraphs.toList,
|
||||
Priority.animation,
|
||||
debugLabel: 'License',
|
||||
);
|
||||
setState(() {
|
||||
_licenses.add(const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 18.0),
|
||||
child: Text(
|
||||
'🍀', // That's U+1F340. Could also use U+2766 (❦) if U+1F340 doesn't work everywhere.
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
));
|
||||
_licenses.add(Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(bottom: BorderSide(width: 0.0))
|
||||
),
|
||||
child: Text(
|
||||
license.packages.join(', '),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
));
|
||||
for (LicenseParagraph paragraph in paragraphs) {
|
||||
if (paragraph.indent == LicenseParagraph.centeredIndent) {
|
||||
_licenses.add(Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: Text(
|
||||
paragraph.text,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
));
|
||||
} else {
|
||||
assert(paragraph.indent >= 0);
|
||||
_licenses.add(Padding(
|
||||
padding: EdgeInsetsDirectional.only(top: 8.0, start: 16.0 * paragraph.indent),
|
||||
child: Text(paragraph.text),
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
setState(() {
|
||||
_loaded = true;
|
||||
});
|
||||
assert(() {
|
||||
dev.Timeline.timeSync('Build scheduled', () { }, flow: dev.Flow.end(debugFlowId));
|
||||
return true;
|
||||
}());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
final String name = widget.applicationName ?? _defaultApplicationName(context);
|
||||
final String version = widget.applicationVersion ?? _defaultApplicationVersion(context);
|
||||
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
|
||||
final List<Widget> contents = <Widget>[
|
||||
Text(name, style: Theme.of(context).textTheme.headline, textAlign: TextAlign.center),
|
||||
Text(version, style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
|
||||
Container(height: 18.0),
|
||||
Text(widget.applicationLegalese ?? '', style: Theme.of(context).textTheme.caption, textAlign: TextAlign.center),
|
||||
Container(height: 18.0),
|
||||
Text('Powered by Flutter', style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
|
||||
Container(height: 24.0),
|
||||
];
|
||||
contents.addAll(_licenses);
|
||||
if (!_loaded) {
|
||||
contents.add(const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 24.0),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
));
|
||||
}
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(localizations.licensesPageTitle),
|
||||
),
|
||||
// All of the licenses page text is English. We don't want localized text
|
||||
// or text direction.
|
||||
body: Localizations.override(
|
||||
locale: const Locale('en', 'US'),
|
||||
context: context,
|
||||
child: DefaultTextStyle(
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: Scrollbar(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
|
||||
children: contents,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _defaultApplicationName(BuildContext context) {
|
||||
// This doesn't handle the case of the application's title dynamically
|
||||
// changing. In theory, we should make Title expose the current application
|
||||
// title using an InheritedWidget, and so forth. However, in practice, if
|
||||
// someone really wants their application title to change dynamically, they
|
||||
// can provide an explicit applicationName to the widgets defined in this
|
||||
// file, instead of relying on the default.
|
||||
final Title ancestorTitle = context.ancestorWidgetOfExactType(Title);
|
||||
return ancestorTitle?.title ?? Platform.resolvedExecutable.split(Platform.pathSeparator).last;
|
||||
}
|
||||
|
||||
String _defaultApplicationVersion(BuildContext context) {
|
||||
// TODO(ianh): Get this from the embedder somehow.
|
||||
return '';
|
||||
}
|
||||
|
||||
Widget _defaultApplicationIcon(BuildContext context) {
|
||||
// TODO(ianh): Get this from the embedder somehow.
|
||||
return null;
|
||||
}
|
61
lib/common/functions/feedback.dart
Normal file
61
lib/common/functions/feedback.dart
Normal file
|
@ -0,0 +1,61 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
final TextEditingController _feedbackController = TextEditingController();
|
||||
|
||||
void feedback(
|
||||
BuildContext context) {
|
||||
// flutter defined function
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
// return object of type Dialog
|
||||
return AlertDialog(
|
||||
title: new Text("Feedback"),
|
||||
content: Column(
|
||||
children: <Widget>[
|
||||
Text("We would love to hear your thoughts."),
|
||||
Text("\nThis section needs to be fixed."),
|
||||
|
||||
Container(
|
||||
margin: const EdgeInsets.fromLTRB(0, 20, 0, 0),
|
||||
width: 200,
|
||||
height: 70,
|
||||
|
||||
child : new TextField(
|
||||
controller: _feedbackController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Enter your feedback here',
|
||||
),
|
||||
// obscureText: true,
|
||||
autocorrect: true,
|
||||
maxLines: 20,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.grey[800],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
onSubmitted: (_) {
|
||||
submit(_feedbackController.text);
|
||||
},
|
||||
),
|
||||
),
|
||||
new Container(
|
||||
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
|
||||
child : new FlatButton(
|
||||
child: new Text("Submit"),
|
||||
onPressed: () {
|
||||
submit(_feedbackController.text);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void submit(String feedback) {
|
||||
debugPrint(feedback);
|
||||
}
|
34
lib/common/functions/showDialogTwoButtons.dart
Normal file
34
lib/common/functions/showDialogTwoButtons.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
void showDialogTwoButtons(
|
||||
BuildContext context, String title, String message, String buttonLabel1, String buttonLabel2, Function action) {
|
||||
// flutter defined function
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
// return object of type Dialog
|
||||
return AlertDialog(
|
||||
title: new Text(title),
|
||||
content: new Text(message),
|
||||
actions: <Widget>[
|
||||
// usually buttons at the bottom of the dialog
|
||||
|
||||
new FlatButton(
|
||||
child: new Text(buttonLabel1),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
|
||||
new FlatButton(
|
||||
child: new Text(buttonLabel2),
|
||||
onPressed: () {
|
||||
action(context);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
|
@ -20,6 +20,7 @@ class PopupListView {
|
|||
for (var i = 0; i < options.length; i++) {
|
||||
dialogOptionsList.add(
|
||||
new SimpleDialogOption(
|
||||
// print each iteration to see if any are null
|
||||
child: Text(options[i]),
|
||||
onPressed: () {
|
||||
Navigator.of(this.context).pop();
|
||||
|
@ -33,8 +34,6 @@ class PopupListView {
|
|||
return dialogOptionsList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Widget dialog() {
|
||||
return new SimpleDialog(
|
||||
title: Text(listTitle),
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:local_spend/pages/login_page.dart';
|
|||
import 'package:local_spend/pages/receipt_page.dart';
|
||||
import 'package:local_spend/pages/spash_screen.dart';
|
||||
import 'package:local_spend/pages/about_screen.dart';
|
||||
import 'package:local_spend/pages/settings.dart';
|
||||
import 'package:local_spend/config.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
|
@ -25,7 +26,7 @@ class MyApp extends StatelessWidget {
|
|||
Locale("en")
|
||||
],
|
||||
|
||||
title: "Splash and Token Authentication",
|
||||
title: "Local Spend Tracker",
|
||||
theme: new ThemeData(
|
||||
primarySwatch: Colors.blueGrey,
|
||||
),
|
||||
|
@ -34,6 +35,7 @@ class MyApp extends StatelessWidget {
|
|||
"/LoginPage": (BuildContext context) => LoginPage(),
|
||||
"/ReceiptPage": (BuildContext context) => ReceiptPage(),
|
||||
"/AboutPage": (BuildContext context) => AboutPage(),
|
||||
"/SettingsPage": (BuildContext context) => SettingsPage(),
|
||||
},
|
||||
home: SplashScreen(),
|
||||
);
|
||||
|
|
|
@ -7,7 +7,10 @@ import 'package:flutter_linkify/flutter_linkify.dart';
|
|||
|
||||
class AboutPage extends StatefulWidget {
|
||||
@override
|
||||
_HomePageState createState() => _HomePageState();
|
||||
// _HomePageState createState() => _HomePageState();
|
||||
State<StatefulWidget> createState() {
|
||||
return new _HomePageState();
|
||||
}
|
||||
}
|
||||
|
||||
class _HomePageState extends State<AboutPage> {
|
||||
|
@ -17,6 +20,11 @@ class _HomePageState extends State<AboutPage> {
|
|||
_saveCurrentRoute("/AboutPage");
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_saveCurrentRoute(String lastRoute) async {
|
||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||
await preferences.setString('LastScreenRoute', lastRoute);
|
||||
|
@ -24,27 +32,36 @@ class _HomePageState extends State<AboutPage> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PlatformScaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
"About Page",
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
color: Colors.black),
|
||||
return WillPopScope(
|
||||
onWillPop: () {
|
||||
if (Navigator.canPop(context)) {
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
'/HomePage', (Route<dynamic> route) => true);
|
||||
} else {
|
||||
Navigator.of(context).pushReplacementNamed('/HomePage');
|
||||
}
|
||||
},
|
||||
child: PlatformScaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
"About Page",
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
leading: BackButton(),
|
||||
centerTitle: true,
|
||||
iconTheme: IconThemeData(color: Colors.black),
|
||||
),
|
||||
centerTitle: true,
|
||||
iconTheme: IconThemeData(color: Colors.black),
|
||||
elevation: Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 6.0,
|
||||
),
|
||||
drawer: BasicDrawer(),
|
||||
body: Container(
|
||||
padding: EdgeInsets.all(32.0),
|
||||
child: ListView(
|
||||
body: Container(
|
||||
padding: EdgeInsets.all(32.0),
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
InkWell(
|
||||
child: const Center(child: Text
|
||||
('Pear Trading',
|
||||
style: TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
color: Colors.blue,
|
||||
),
|
||||
|
@ -113,6 +130,7 @@ class _HomePageState extends State<AboutPage> {
|
|||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,94 +1,61 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:local_spend/common/platform/platform_scaffold.dart';
|
||||
import 'package:local_spend/common/widgets/basic_drawer.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:local_spend/common/apifunctions/request_logout_api.dart';
|
||||
import 'package:local_spend/common/functions/get_token.dart';
|
||||
import 'package:flutter_fadein/flutter_fadein.dart';
|
||||
import 'package:local_spend/common/functions/logout.dart';
|
||||
import 'package:local_spend/pages/receipt_page.dart';
|
||||
import 'package:local_spend/pages/settings.dart';
|
||||
|
||||
class HomePage extends StatelessWidget {
|
||||
static String _title = 'Text here';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: _title,
|
||||
home: HomePageWidget(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HomePageWidget extends StatefulWidget {
|
||||
HomePageWidget({Key key}) : super(key: key);
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
@override
|
||||
_HomePageState createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
@override
|
||||
class _HomePageState extends State<HomePageWidget> {
|
||||
int _selectedIndex = 0;
|
||||
static const TextStyle optionStyle =
|
||||
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
|
||||
static List<Widget> _widgetOptions = <Widget>[
|
||||
ReceiptPage(),
|
||||
SettingsPage()
|
||||
];
|
||||
|
||||
void initState() {
|
||||
super.initState();
|
||||
_saveCurrentRoute("/HomePage");
|
||||
}
|
||||
|
||||
_saveCurrentRoute(String lastRoute) async {
|
||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||
await preferences.setString('LastScreenRoute', lastRoute);
|
||||
void _onItemTapped(int index) {
|
||||
setState(() {
|
||||
_selectedIndex = index;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PlatformScaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
"Navigation",
|
||||
style: TextStyle(color: Colors.black),
|
||||
),
|
||||
iconTheme: IconThemeData(color: Colors.black),
|
||||
elevation: Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 6.0,
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: _widgetOptions.elementAt(_selectedIndex),
|
||||
),
|
||||
drawer: BasicDrawer(),
|
||||
body: Container(
|
||||
padding: EdgeInsets.fromLTRB(0, 15, 0, 0),
|
||||
child: FadeIn(
|
||||
duration: Duration(milliseconds: 500),
|
||||
curve: Curves.easeIn,
|
||||
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
|
||||
ListTile(
|
||||
title: new Center(
|
||||
child: new Text(
|
||||
"Submit Receipt",
|
||||
style: TextStyle(color: Colors.black, fontSize: 20.0),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
// debugPrint('$token');
|
||||
Navigator.of(context).pushNamed('/ReceiptPage');
|
||||
},
|
||||
),
|
||||
|
||||
ListTile(
|
||||
title: new Center(
|
||||
child: new Text(
|
||||
"About",
|
||||
style: TextStyle(color: Colors.black, fontSize: 20.0),
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
SystemChannels.textInput.invokeMethod('TextInput.hide');
|
||||
Navigator.of(context).pushReplacementNamed('/AboutPage');
|
||||
},
|
||||
),
|
||||
|
||||
ListTile(
|
||||
title: new Center(
|
||||
child: new Text(
|
||||
"Logout",
|
||||
style: TextStyle(color: Colors.black, fontSize: 20.0),
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
logout(context);
|
||||
},
|
||||
|
||||
),
|
||||
],
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
items: const <BottomNavigationBarItem>[
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.receipt),
|
||||
title: Text('Submit Receipt'),
|
||||
),
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.settings),
|
||||
title: Text('Settings'),
|
||||
),
|
||||
],
|
||||
currentIndex: _selectedIndex,
|
||||
selectedItemColor: Colors.blue[400],
|
||||
onTap: _onItemTapped,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ class LoginPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class LoginPageState extends State<LoginPage> {
|
||||
final TextEditingController _emailController = TextEditingController(/*text: 'test@example.com'*/); // remove
|
||||
final TextEditingController _passwordController = TextEditingController(/*text: 'abc123'*/); // remove
|
||||
final TextEditingController _emailController = TextEditingController(text: 'test@example.com'); // remove
|
||||
final TextEditingController _passwordController = TextEditingController(text: 'abc123'); // remove
|
||||
bool _saveLoginDetails = true; // I am extremely sorry for the placement of this variable
|
||||
// it will be fixed soon I promise
|
||||
|
||||
|
|
|
@ -9,9 +9,11 @@ import 'package:local_spend/common/widgets/basic_drawer.dart';
|
|||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:local_spend/pages/settings.dart';
|
||||
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
|
||||
import 'package:local_spend/common/apifunctions/find_organisations.dart';
|
||||
import 'package:local_spend/common/widgets/popupListView.dart';
|
||||
import 'package:local_spend/common/widgets/labeled_checkbox.dart';
|
||||
|
||||
const URL = "https://flutter.io/";
|
||||
const demonstration = false;
|
||||
|
@ -30,6 +32,7 @@ class ReceiptPageState extends State<ReceiptPage> {
|
|||
final TextEditingController _recurringController = TextEditingController();
|
||||
final TextEditingController _typeController = TextEditingController();
|
||||
final TextEditingController _orgController = TextEditingController();
|
||||
bool _recurringCheckbox = false; // have mercy, this will be removed. sorry for this variable's placement...
|
||||
|
||||
FocusNode focusNode; // added so focus can move automatically
|
||||
|
||||
|
@ -174,223 +177,310 @@ class ReceiptPageState extends State<ReceiptPage> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var drawer = Drawer();
|
||||
return WillPopScope(
|
||||
onWillPop: () {
|
||||
if (Navigator.canPop(context)) {
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
'/HomePage', (Route<dynamic> route) => false);
|
||||
'/LoginPage', (Route<dynamic> route) => false);
|
||||
} else {
|
||||
Navigator.of(context).pushReplacementNamed('/HomePage');
|
||||
Navigator.of(context).pushReplacementNamed('/LoginPage');
|
||||
}
|
||||
},
|
||||
child: PlatformScaffold(
|
||||
drawer: BasicDrawer(),
|
||||
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.blue[400],
|
||||
title: Text(
|
||||
"Submit Receipt",
|
||||
style: TextStyle(
|
||||
fontSize: 30.0,
|
||||
fontSize: 20,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
// leading: BackButton(),
|
||||
centerTitle: true,
|
||||
iconTheme: IconThemeData(color: Colors.black),
|
||||
),
|
||||
|
||||
body: Container(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(30.0, 0.0, 30.0, 0.0),
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
// Container(
|
||||
// alignment: Alignment.topCenter,
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 15.0),
|
||||
// child: Text(
|
||||
// "Required fields are in bold",
|
||||
// style: TextStyle(fontSize: 20.0, color: Colors.black),
|
||||
// ),
|
||||
// )),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
||||
child : Text(
|
||||
"Time of Transaction",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
padding: EdgeInsets.fromLTRB(30.0, 0.0, 30.0, 0.0),
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
||||
child : Text(
|
||||
"Time of Transaction",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
DateTimePickerFormField(
|
||||
inputType: InputType.both,
|
||||
format: DateFormat("dd/MM/yyyy 'at' hh:mm"),
|
||||
editable: true,
|
||||
controller: _timeController,
|
||||
),
|
||||
DateTimePickerFormField(
|
||||
inputType: InputType.both,
|
||||
format: DateFormat("dd/MM/yyyy 'at' hh:mm"),
|
||||
editable: true,
|
||||
controller: _timeController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Date/Time of Transaction', hasFloatingPlaceholder: false),
|
||||
onChanged: (dt) => setState(() => date = dt),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
||||
child: Text(
|
||||
"Amount",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 0.0),
|
||||
child: TextField(
|
||||
controller: _amountController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Date/Time of Transaction', hasFloatingPlaceholder: false),
|
||||
onChanged: (dt) => setState(() => date = dt),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
||||
child: Text(
|
||||
"Amount",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
hintText: 'Value in £',
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 0.0),
|
||||
child: TextField(
|
||||
controller: _amountController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Value in £',
|
||||
),
|
||||
// obscureText: true,
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.number,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.grey[800],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
onSubmitted: (_) {
|
||||
FocusScope.of(context).requestFocus(focusNode);
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.number,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.grey[800],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
onSubmitted: (_) {
|
||||
FocusScope.of(context).requestFocus(focusNode);
|
||||
// submitReceipt(_amountController.text, _timeController.text);
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
||||
|
||||
child : Container (
|
||||
height: 22, // this should be the same height as text
|
||||
child : Container (
|
||||
height: 22, // this should be the same height as text
|
||||
|
||||
child : ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: <Widget>[
|
||||
child : ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: <Widget>[
|
||||
|
||||
Container(
|
||||
child: Text(
|
||||
"Organization Name",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
Container(
|
||||
child: Text(
|
||||
"Organization Name",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
child : Padding(
|
||||
padding: EdgeInsets.fromLTRB(5,0,0,4), // sorry about hardcoded constraints
|
||||
child: FlatButton(
|
||||
onPressed: () {
|
||||
var organisations = findOrganisations(_orgController.text);
|
||||
// some tasty async stuff here yum yum
|
||||
// and a pretty little dialog too yay
|
||||
var choice = organisations.then((data) => listOrganisations(data, context));
|
||||
Container(
|
||||
child : Padding(
|
||||
padding: EdgeInsets.fromLTRB(5,0,0,4), // sorry about hardcoded constraints
|
||||
child: FlatButton(
|
||||
onPressed: () {
|
||||
var organisations = findOrganisations(_orgController.text);
|
||||
// some tasty async stuff here yum yum
|
||||
// and a pretty little dialog too yay (doesn't work)
|
||||
var choice = organisations.then((data) => listOrganisations(data, context));
|
||||
|
||||
// choice is a Future<String>
|
||||
},
|
||||
child: Text("Find",
|
||||
style:
|
||||
TextStyle(color: Colors.blue, fontSize: 18.0)),
|
||||
),
|
||||
),
|
||||
)
|
||||
// choice is a Future<String>
|
||||
},
|
||||
child: Text("Find",
|
||||
style:
|
||||
TextStyle(color: Colors.blue, fontSize: 18.0)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 0.0),
|
||||
child: TextField(
|
||||
controller: _orgController,
|
||||
focusNode: focusNode,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Eg. Pear Trading',
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 0.0),
|
||||
child: TextField(
|
||||
controller: _orgController,
|
||||
focusNode: focusNode,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Eg. Pear Trading',
|
||||
),
|
||||
// obscureText: true,
|
||||
autocorrect: true,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.grey[800],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
onSubmitted: (_) {
|
||||
submitReceipt(_amountController.text,
|
||||
_timeController.text, _orgController.text);
|
||||
// TODO: make sure organisation is valid
|
||||
// TODO: Add 'find organisation' button which displays a dialog to, well, find the organisation's address or manual entry
|
||||
autocorrect: true,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.grey[800],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
onSubmitted: (_) {
|
||||
submitReceipt(_amountController.text,
|
||||
_timeController.text, _orgController.text);
|
||||
// TODO: make sure organisation is valid
|
||||
// TODO: Add 'find organisation' button which displays a dialog to, well, find the organisation's address or manual entry
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
||||
|
||||
child : Container (
|
||||
height: 18,
|
||||
|
||||
child : ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Text(
|
||||
"Essential",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
child : Padding(
|
||||
padding: EdgeInsets.fromLTRB(20.0, 0.0, 0, 0),
|
||||
|
||||
child: Checkbox(value:
|
||||
_essentialController.text.toLowerCase() == 'true',
|
||||
onChanged: (bool newValue) {
|
||||
setState(() {
|
||||
_essentialController.text =
|
||||
convertBoolToString(newValue);
|
||||
});
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
||||
|
||||
child : Container (
|
||||
height: 27,
|
||||
// width: 400,
|
||||
|
||||
child : ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Text(
|
||||
"Recurring",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
child : Padding(
|
||||
padding: EdgeInsets.fromLTRB(15.0, 0.0, 0, 4),
|
||||
|
||||
child: Checkbox(value:
|
||||
_essentialController.text.toLowerCase() != "none" ||
|
||||
_essentialController.text.toLowerCase() != 'false',
|
||||
onChanged: (bool newValue) {
|
||||
setState(() {
|
||||
var options = new List<String>(7);
|
||||
options[0] = "Daily";
|
||||
options[1] = "Weekly";
|
||||
options[2] = "Fortnightly";
|
||||
options[3] = "Monthly";
|
||||
options[5] = "Quarterly";
|
||||
options[6] = "Yearly";
|
||||
|
||||
var popupListView = new PopupListView(
|
||||
context, options, "Recurring...");
|
||||
|
||||
var dialog = popupListView.dialog();
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return dialog;
|
||||
},
|
||||
);
|
||||
|
||||
print(popupListView.result);
|
||||
// _recurringController.text =
|
||||
// popupListView.result;
|
||||
});
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
padding: EdgeInsets.fromLTRB(10, 2, 0, 0),
|
||||
child: Text(
|
||||
convertBoolToString(_essentialController.text.toLowerCase() != "none" ||
|
||||
_essentialController.text.toLowerCase() != 'false'),
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
// var options = new List<String>(1);
|
||||
// options[0] = "Weekly";
|
||||
//
|
||||
// var popupListView = new PopupListView(context, options, "Recurring...");
|
||||
//
|
||||
// var dialog = popupListView.dialog();
|
||||
//
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (BuildContext context) {
|
||||
// return dialog;
|
||||
// },
|
||||
// );
|
||||
//
|
||||
// print(popupListView.result);
|
||||
// _recurringController.text = popupListView.result;
|
||||
//
|
||||
// setState(() {
|
||||
// _recurringController.text =
|
||||
// convertBoolToString(newValue);
|
||||
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 0.0),
|
||||
child: Container(
|
||||
height: 65.0,
|
||||
child: RaisedButton(
|
||||
onPressed: () {
|
||||
submitReceipt(_amountController.text, _timeController.text, _orgController.text);
|
||||
},
|
||||
child: Text("SUBMIT",
|
||||
style:
|
||||
TextStyle(color: Colors.white, fontSize: 22.0)),
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
||||
|
||||
child : Container (
|
||||
height: 18,
|
||||
|
||||
child : ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Text(
|
||||
"Essential Purchase",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
child : Padding(
|
||||
padding: EdgeInsets.fromLTRB(20.0, 0.0, 0, 0),
|
||||
|
||||
child: Checkbox(value:
|
||||
_essentialController.text.toLowerCase() == 'true',
|
||||
onChanged: (bool newValue) {
|
||||
setState(() {
|
||||
_essentialController.text =
|
||||
convertBoolToString(newValue);
|
||||
});
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, 70.0, 0.0, 0.0),
|
||||
child: Container(
|
||||
height: 65.0,
|
||||
child: RaisedButton(
|
||||
onPressed: () {
|
||||
submitReceipt(_amountController.text, _timeController.text, _orgController.text);
|
||||
},
|
||||
child: Text("SUBMIT",
|
||||
style:
|
||||
TextStyle(color: Colors.white, fontSize: 22.0)),
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
180
lib/pages/settings.dart
Normal file
180
lib/pages/settings.dart
Normal file
|
@ -0,0 +1,180 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:local_spend/common/platform/platform_scaffold.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:local_spend/common/functions/logout.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:local_spend/common/functions/customAbout.dart' as custom;
|
||||
import 'package:local_spend/common/functions/showDialogTwoButtons.dart';
|
||||
import 'package:local_spend/common/functions/feedback.dart';
|
||||
|
||||
const URL = "https://flutter.io/";
|
||||
const demonstration = false;
|
||||
|
||||
class SettingsPage extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return new SettingsPageState();
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsPageState extends State<SettingsPage> {
|
||||
FocusNode focusNode; // added so focus can move automatically
|
||||
|
||||
DateTime date;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_saveCurrentRoute("/SettingsPageState");
|
||||
|
||||
focusNode = FocusNode();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
|
||||
focusNode.dispose(); //disposes focus node when form disposed
|
||||
}
|
||||
|
||||
_saveCurrentRoute(String lastRoute) async {
|
||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||
await preferences.setString('LastPageRoute', lastRoute);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
onWillPop: () {
|
||||
if (Navigator.canPop(context)) {
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
'/LoginPage', (Route<dynamic> route) => false);
|
||||
} else {
|
||||
Navigator.of(context).pushReplacementNamed('/LoginPage');
|
||||
}
|
||||
},
|
||||
child: PlatformScaffold(
|
||||
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.blue[400],
|
||||
title: Text(
|
||||
"Settings",
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
// leading: BackButton(),
|
||||
centerTitle: true,
|
||||
iconTheme: IconThemeData(color: Colors.black),
|
||||
),
|
||||
|
||||
body: Container(
|
||||
padding: EdgeInsets.fromLTRB(30.0, 0.0, 30.0, 0.0),
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
|
||||
Container(
|
||||
padding: EdgeInsets.fromLTRB(0.0,25,0.0,0.0),
|
||||
child : Text(
|
||||
"Local Spend Tracker",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 22.0,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
//
|
||||
// Padding(
|
||||
// padding: EdgeInsets.fromLTRB(10, 15, 0, 0),
|
||||
// child: Text("helloooo"),
|
||||
// ),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, 25.0, 0.0, 0.0),
|
||||
child: Container(
|
||||
height: 65.0,
|
||||
child: RaisedButton(
|
||||
onPressed: () {
|
||||
|
||||
custom.showAboutDialog(
|
||||
context: context,
|
||||
applicationIcon: new Icon(Icons.receipt),
|
||||
applicationName: "Local Spend Tracker",
|
||||
children: <Widget> [
|
||||
Text("Pear Trading is a commerce company designed to register and monitor money circulating in the local economy."),
|
||||
Text("\nContact at test@example.com or +44(0)1524 64544"),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0,20,0,0),
|
||||
child: InkWell(
|
||||
child: Text
|
||||
('Developed by Shadowcat Systems',
|
||||
style: TextStyle(
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
onTap: () => launch('https://shadow.cat/')
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
},
|
||||
child: Text("ABOUT",
|
||||
style:
|
||||
TextStyle(color: Colors.white, fontSize: 22.0)),
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
|
||||
child: Container(
|
||||
height: 65.0,
|
||||
child: RaisedButton(
|
||||
onPressed: () {
|
||||
showDialogTwoButtons(
|
||||
context,
|
||||
"Logout",
|
||||
"Are you sure you want to log out?",
|
||||
"Cancel",
|
||||
"Logout",
|
||||
logout
|
||||
);
|
||||
},
|
||||
child: Text("LOGOUT",
|
||||
style:
|
||||
TextStyle(color: Colors.white, fontSize: 22.0)),
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
|
||||
child: Container(
|
||||
height: 65.0,
|
||||
child: RaisedButton(
|
||||
onPressed: () {
|
||||
feedback(context);
|
||||
},
|
||||
child: Text("FEEDBACK",
|
||||
style:
|
||||
TextStyle(color: Colors.white, fontSize: 22.0)),
|
||||
color: Colors.green,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Reference in a new issue