[UI craze] begin implementing new UI
This commit is contained in:
parent
34786d5de3
commit
90fad4a0ac
13 changed files with 474 additions and 153 deletions
22
lib/components/appBar.dart
Normal file
22
lib/components/appBar.dart
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
|
const FAppBar({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
var colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
child: AppBar(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
elevation: 0,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Size get preferredSize => Size.fromHeight(56);
|
||||||
|
}
|
40
lib/components/button.dart
Normal file
40
lib/components/button.dart
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FButton extends StatelessWidget {
|
||||||
|
final String labelText;
|
||||||
|
final double padding;
|
||||||
|
final double insidePadding;
|
||||||
|
final double fontSize;
|
||||||
|
final Function()? onPressed;
|
||||||
|
|
||||||
|
const FButton({super.key, required this.labelText, this.padding = 8, this.insidePadding = 24, this.fontSize = 20, this.onPressed});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
var colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onPressed,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: padding, horizontal: padding),
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: insidePadding),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colorScheme.primary,
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
labelText,
|
||||||
|
style: theme.textTheme.button!.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: fontSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
135
lib/components/datePicker.dart
Normal file
135
lib/components/datePicker.dart
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class FDateItemWidget extends StatelessWidget {
|
||||||
|
final DateTime date;
|
||||||
|
final bool picked;
|
||||||
|
final Function(DateTime) onDatePicked;
|
||||||
|
|
||||||
|
const FDateItemWidget({super.key, required this.date, required this.onDatePicked, this.picked = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
var colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
|
var dayOfTheWeekMap = {
|
||||||
|
1: 'Mon',
|
||||||
|
2: 'Tue',
|
||||||
|
3: 'Wed',
|
||||||
|
4: 'Thu',
|
||||||
|
5: 'Fri',
|
||||||
|
6: 'Sat',
|
||||||
|
7: 'Sun',
|
||||||
|
};
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
onDatePicked(date);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 100,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(50),
|
||||||
|
border: Border.all(
|
||||||
|
color: colorScheme.onPrimary,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
color: picked ? colorScheme.onPrimary.withOpacity(0.25) : Colors.transparent,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
dayOfTheWeekMap[date.weekday]!,
|
||||||
|
style: TextStyle(
|
||||||
|
color: colorScheme.onPrimary,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${date.day}.${date.month}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: colorScheme.onPrimary,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FDatePickerWidget extends StatefulWidget {
|
||||||
|
final DateTime date;
|
||||||
|
final Function(DateTime) onDatePicked;
|
||||||
|
|
||||||
|
const FDatePickerWidget({super.key, required this.date, required this.onDatePicked});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FDatePickerWidget> createState() => _FDatePickerWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FDatePickerWidgetState extends State<FDatePickerWidget> {
|
||||||
|
DateTime date = DateTime.now();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
setState(() {
|
||||||
|
date = widget.date;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> onDatePicked(DateTime date) async {
|
||||||
|
setState(() {
|
||||||
|
this.date = date;
|
||||||
|
});
|
||||||
|
|
||||||
|
await widget.onDatePicked(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
var colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
height: 100,
|
||||||
|
child: Center(
|
||||||
|
child: ListView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
shrinkWrap: true,
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(width: 25),
|
||||||
|
FDateItemWidget(date: date.add(Duration(days: -2)), onDatePicked: onDatePicked),
|
||||||
|
SizedBox(width: 25),
|
||||||
|
FDateItemWidget(date: date.add(Duration(days: -1)), onDatePicked: onDatePicked),
|
||||||
|
SizedBox(width: 25),
|
||||||
|
FDateItemWidget(date: date, onDatePicked: onDatePicked, picked: true),
|
||||||
|
SizedBox(width: 25),
|
||||||
|
FDateItemWidget(date: date.add(Duration(days: 1)), onDatePicked: onDatePicked),
|
||||||
|
SizedBox(width: 25),
|
||||||
|
FDateItemWidget(date: date.add(Duration(days: 2)), onDatePicked: onDatePicked),
|
||||||
|
Container(
|
||||||
|
width: 100,
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.calendar_month,
|
||||||
|
color: colorScheme.onPrimary,
|
||||||
|
size: 40,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
onDatePicked(date.add(Duration(days: 1)));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
99
lib/components/sliver.dart
Normal file
99
lib/components/sliver.dart
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class BackgroundWave extends StatelessWidget {
|
||||||
|
final double height;
|
||||||
|
|
||||||
|
const BackgroundWave({Key? key, required this.height}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
var colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
height: height,
|
||||||
|
child: ClipPath(
|
||||||
|
clipper: BackgroundWaveClipper(),
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: height,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [colorScheme.primary, colorScheme.secondary],
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BackgroundWaveClipper extends CustomClipper<Path> {
|
||||||
|
@override
|
||||||
|
Path getClip(Size size) {
|
||||||
|
var path = Path();
|
||||||
|
path.lineTo(0.0, size.height);
|
||||||
|
|
||||||
|
var firstCurve = Offset(0, size.height - 20);
|
||||||
|
var lastCurve = Offset(30, size.height - 20);
|
||||||
|
|
||||||
|
path.quadraticBezierTo(
|
||||||
|
firstCurve.dx, firstCurve.dy, lastCurve.dx, lastCurve.dy,
|
||||||
|
);
|
||||||
|
|
||||||
|
firstCurve = Offset(0, size.height - 20);
|
||||||
|
lastCurve = Offset(size.width - 30, size.height - 20);
|
||||||
|
|
||||||
|
path.quadraticBezierTo(
|
||||||
|
firstCurve.dx, firstCurve.dy, lastCurve.dx, lastCurve.dy,
|
||||||
|
);
|
||||||
|
|
||||||
|
firstCurve = Offset(size.width, size.height - 20);
|
||||||
|
lastCurve = Offset(size.width, size.height);
|
||||||
|
|
||||||
|
path.quadraticBezierTo(
|
||||||
|
firstCurve.dx, firstCurve.dy, lastCurve.dx, lastCurve.dy,
|
||||||
|
);
|
||||||
|
|
||||||
|
path.lineTo(size.width, 0.0);
|
||||||
|
path.close();
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldReclip(BackgroundWaveClipper oldClipper) => oldClipper != this;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FSliverAppBar extends SliverPersistentHeaderDelegate {
|
||||||
|
final Widget child;
|
||||||
|
const FSliverAppBar({required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||||
|
var adjustedShrinkOffset = shrinkOffset > minExtent ? minExtent : shrinkOffset;
|
||||||
|
double offset = (minExtent - adjustedShrinkOffset);
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
const BackgroundWave(
|
||||||
|
height: 280,
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: offset + 8,
|
||||||
|
child: child,
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
double get maxExtent => 280;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double get minExtent => 140;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
|
||||||
|
oldDelegate.maxExtent != maxExtent || oldDelegate.minExtent != minExtent;
|
||||||
|
}
|
39
lib/components/text.dart
Normal file
39
lib/components/text.dart
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FTextInput extends StatelessWidget {
|
||||||
|
final String labelText;
|
||||||
|
final double padding;
|
||||||
|
final TextEditingController controller;
|
||||||
|
final List<String>? autofillHints;
|
||||||
|
final bool autofocus;
|
||||||
|
final bool obscureText;
|
||||||
|
final Function(String)? onFieldSubmitted;
|
||||||
|
|
||||||
|
const FTextInput({super.key, required this.labelText, this.padding = 8, required this.controller, this.autofillHints, this.autofocus = false, this.onFieldSubmitted, this.obscureText = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
var colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: padding, horizontal: padding),
|
||||||
|
child: TextFormField(
|
||||||
|
obscureText: obscureText,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: labelText,
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: colorScheme.primary),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: colorScheme.onPrimary),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
controller: controller,
|
||||||
|
autofillHints: autofillHints,
|
||||||
|
autofocus: autofocus,
|
||||||
|
onFieldSubmitted: onFieldSubmitted,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fooder/screens/login.dart';
|
import 'package:fooder/screens/login.dart';
|
||||||
import 'package:fooder/client.dart';
|
import 'package:fooder/client.dart';
|
||||||
|
import 'package:flex_color_scheme/flex_color_scheme.dart';
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
@ -9,13 +10,10 @@ class MyApp extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'FOODER',
|
title: 'FOODER',
|
||||||
theme: ThemeData(
|
theme: FlexThemeData.light(scheme: FlexScheme.brandBlue),
|
||||||
colorScheme: ColorScheme.fromSeed(
|
darkTheme: FlexThemeData.dark(scheme: FlexScheme.brandBlue),
|
||||||
seedColor: Colors.blueGrey,
|
themeMode: ThemeMode.system,
|
||||||
brightness: Brightness.dark,
|
debugShowCheckedModeBanner: false,
|
||||||
),
|
|
||||||
useMaterial3: true,
|
|
||||||
),
|
|
||||||
home: LoginScreen(
|
home: LoginScreen(
|
||||||
apiClient: ApiClient(
|
apiClient: ApiClient(
|
||||||
baseUrl: 'https://fooderapi.domandoman.xyz/api',
|
baseUrl: 'https://fooderapi.domandoman.xyz/api',
|
||||||
|
|
|
@ -7,6 +7,8 @@ import 'package:fooder/models/meal.dart';
|
||||||
import 'package:fooder/widgets/product.dart';
|
import 'package:fooder/widgets/product.dart';
|
||||||
import 'package:fooder/screens/add_product.dart';
|
import 'package:fooder/screens/add_product.dart';
|
||||||
import 'package:simple_barcode_scanner/simple_barcode_scanner.dart';
|
import 'package:simple_barcode_scanner/simple_barcode_scanner.dart';
|
||||||
|
import 'package:fooder/components/appBar.dart';
|
||||||
|
|
||||||
|
|
||||||
class AddEntryScreen extends BasedScreen {
|
class AddEntryScreen extends BasedScreen {
|
||||||
final Diary diary;
|
final Diary diary;
|
||||||
|
@ -126,10 +128,8 @@ class _AddEntryScreen extends State<AddEntryScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
extendBodyBehindAppBar: true,
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
appBar: FAppBar(),
|
||||||
title: Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context)),
|
|
||||||
),
|
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 720),
|
constraints: const BoxConstraints(maxWidth: 720),
|
||||||
|
|
|
@ -11,5 +11,36 @@ abstract class BasedScreen extends StatefulWidget {
|
||||||
final ApiClient apiClient;
|
final ApiClient apiClient;
|
||||||
|
|
||||||
const BasedScreen({super.key, required this.apiClient});
|
const BasedScreen({super.key, required this.apiClient});
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BasedState<T extends BasedScreen> extends State<T> {
|
||||||
|
void showError(String message) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
message,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showText(String text) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
text,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:fooder/screens/based.dart';
|
import 'package:fooder/screens/based.dart';
|
||||||
import 'package:fooder/screens/main.dart';
|
import 'package:fooder/screens/main.dart';
|
||||||
import 'package:fooder/screens/register.dart';
|
import 'package:fooder/screens/register.dart';
|
||||||
|
import 'package:fooder/components/text.dart';
|
||||||
|
import 'package:fooder/components/button.dart';
|
||||||
|
|
||||||
class LoginScreen extends BasedScreen {
|
class LoginScreen extends BasedScreen {
|
||||||
const LoginScreen({super.key, required super.apiClient});
|
const LoginScreen({super.key, required super.apiClient});
|
||||||
|
@ -11,7 +13,7 @@ class LoginScreen extends BasedScreen {
|
||||||
State<LoginScreen> createState() => _LoginScreen();
|
State<LoginScreen> createState() => _LoginScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LoginScreen extends State<LoginScreen> {
|
class _LoginScreen extends BasedState<LoginScreen> {
|
||||||
final usernameController = TextEditingController();
|
final usernameController = TextEditingController();
|
||||||
final passwordController = TextEditingController();
|
final passwordController = TextEditingController();
|
||||||
|
|
||||||
|
@ -22,24 +24,6 @@ class _LoginScreen extends State<LoginScreen> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void showError(String message) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(message, textAlign: TextAlign.center),
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.error,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showText(String text) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(text, textAlign: TextAlign.center),
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void popMeDaddy() {
|
void popMeDaddy() {
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
|
@ -95,11 +79,10 @@ class _LoginScreen extends State<LoginScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
var colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
|
||||||
title: Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context)),
|
|
||||||
),
|
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 600),
|
constraints: const BoxConstraints(maxWidth: 600),
|
||||||
|
@ -108,32 +91,30 @@ class _LoginScreen extends State<LoginScreen> {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
TextFormField(
|
Icon(
|
||||||
decoration: const InputDecoration(
|
Icons.lock,
|
||||||
labelText: 'Username',
|
size: 100,
|
||||||
),
|
color: colorScheme.primary,
|
||||||
|
),
|
||||||
|
FTextInput(
|
||||||
|
labelText: 'Username',
|
||||||
controller: usernameController,
|
controller: usernameController,
|
||||||
autofillHints: const [AutofillHints.username],
|
autofillHints: const [AutofillHints.username],
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
),
|
),
|
||||||
TextFormField(
|
FTextInput(
|
||||||
obscureText: true,
|
labelText: 'Password',
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'Password',
|
|
||||||
),
|
|
||||||
controller: passwordController,
|
controller: passwordController,
|
||||||
onFieldSubmitted: (_) => _login(),
|
onFieldSubmitted: (_) => _login(),
|
||||||
autofillHints: const [AutofillHints.password],
|
autofillHints: const [AutofillHints.password],
|
||||||
|
obscureText: true,
|
||||||
|
),
|
||||||
|
FButton(
|
||||||
|
labelText: 'Sign In',
|
||||||
|
onPressed: _login,
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
child: FilledButton(
|
|
||||||
onPressed: _login,
|
|
||||||
child: const Text('Login'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
|
|
|
@ -4,6 +4,10 @@ import 'package:fooder/screens/login.dart';
|
||||||
import 'package:fooder/screens/add_entry.dart';
|
import 'package:fooder/screens/add_entry.dart';
|
||||||
import 'package:fooder/models/diary.dart';
|
import 'package:fooder/models/diary.dart';
|
||||||
import 'package:fooder/widgets/diary.dart';
|
import 'package:fooder/widgets/diary.dart';
|
||||||
|
import 'package:fooder/widgets/meal.dart';
|
||||||
|
import 'package:fooder/components/appBar.dart';
|
||||||
|
import 'package:fooder/components/sliver.dart';
|
||||||
|
import 'package:fooder/components/datePicker.dart';
|
||||||
|
|
||||||
class MainScreen extends BasedScreen {
|
class MainScreen extends BasedScreen {
|
||||||
const MainScreen({super.key, required super.apiClient});
|
const MainScreen({super.key, required super.apiClient});
|
||||||
|
@ -12,7 +16,7 @@ class MainScreen extends BasedScreen {
|
||||||
State<MainScreen> createState() => _MainScreen();
|
State<MainScreen> createState() => _MainScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MainScreen extends State<MainScreen> {
|
class _MainScreen extends BasedState<MainScreen> {
|
||||||
Diary? diary;
|
Diary? diary;
|
||||||
DateTime date = DateTime.now();
|
DateTime date = DateTime.now();
|
||||||
|
|
||||||
|
@ -24,19 +28,18 @@ class _MainScreen extends State<MainScreen> {
|
||||||
|
|
||||||
Future<void> _asyncInitState() async {
|
Future<void> _asyncInitState() async {
|
||||||
var diaryMap = await widget.apiClient.getDiary(date: date);
|
var diaryMap = await widget.apiClient.getDiary(date: date);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
diary = Diary.fromJson(diaryMap);
|
diary = Diary.fromJson(diaryMap);
|
||||||
date = date;
|
date = date;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pickDate() async {
|
Future<void> _pickDate(DateTime date) async {
|
||||||
date = (await showDatePicker(
|
setState(() {
|
||||||
context: context,
|
this.date = date;
|
||||||
initialDate: date,
|
});
|
||||||
firstDate: DateTime(2020),
|
|
||||||
lastDate: DateTime(DateTime.now().year + 1),
|
|
||||||
))!;
|
|
||||||
await _asyncInitState();
|
await _asyncInitState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,60 +69,34 @@ class _MainScreen extends State<MainScreen> {
|
||||||
Widget title;
|
Widget title;
|
||||||
|
|
||||||
if (diary != null) {
|
if (diary != null) {
|
||||||
content = Container(
|
content = CustomScrollView(
|
||||||
constraints: const BoxConstraints(maxWidth: 720),
|
slivers: <Widget>[
|
||||||
padding: const EdgeInsets.all(10),
|
SliverPersistentHeader(
|
||||||
child: DiaryWidget(
|
delegate: FSliverAppBar(child: FDatePickerWidget(date: date, onDatePicked: _pickDate)),
|
||||||
diary: diary!,
|
pinned: true,
|
||||||
apiClient: widget.apiClient,
|
),
|
||||||
refreshParent: _asyncInitState),
|
SliverList(
|
||||||
);
|
delegate: SliverChildListDelegate(
|
||||||
title = Row(
|
[
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
for (var meal in diary!.meals)
|
||||||
children: <Widget>[
|
MealWidget(
|
||||||
TextButton(
|
meal: meal,
|
||||||
child: Text(
|
apiClient: widget.apiClient,
|
||||||
"🅵🅾🅾🅳🅴🆁",
|
refreshParent: _asyncInitState,
|
||||||
style: logoStyle(context),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
onPressed: () {
|
|
||||||
Navigator.pushReplacement(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
MainScreen(apiClient: widget.apiClient)),
|
|
||||||
).then((_) => _asyncInitState());
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
const Spacer(),
|
]
|
||||||
Text(
|
|
||||||
"${date.year}-${date.month}-${date.day}",
|
|
||||||
style: const TextStyle(fontSize: 20),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.calendar_month),
|
|
||||||
onPressed: _pickDate,
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.logout),
|
|
||||||
onPressed: _logout,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
content = const CircularProgressIndicator();
|
content = const Center(child: const CircularProgressIndicator());
|
||||||
title = Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
body: content,
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
extendBodyBehindAppBar: true,
|
||||||
title: title,
|
appBar: FAppBar(),
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
child: content,
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: _addEntry,
|
onPressed: _addEntry,
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:fooder/screens/based.dart';
|
import 'package:fooder/screens/based.dart';
|
||||||
|
import 'package:fooder/components/text.dart';
|
||||||
|
import 'package:fooder/components/button.dart';
|
||||||
|
|
||||||
class RegisterScreen extends BasedScreen {
|
class RegisterScreen extends BasedScreen {
|
||||||
const RegisterScreen({super.key, required super.apiClient});
|
const RegisterScreen({super.key, required super.apiClient});
|
||||||
|
@ -9,7 +11,7 @@ class RegisterScreen extends BasedScreen {
|
||||||
State<RegisterScreen> createState() => _RegisterScreen();
|
State<RegisterScreen> createState() => _RegisterScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RegisterScreen extends State<RegisterScreen> {
|
class _RegisterScreen extends BasedState<RegisterScreen> {
|
||||||
final usernameController = TextEditingController();
|
final usernameController = TextEditingController();
|
||||||
final passwordController = TextEditingController();
|
final passwordController = TextEditingController();
|
||||||
final passwordConfirmController = TextEditingController();
|
final passwordConfirmController = TextEditingController();
|
||||||
|
@ -35,24 +37,6 @@ class _RegisterScreen extends State<RegisterScreen> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void showError(String message) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(message, textAlign: TextAlign.center),
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.error,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showText(String text) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(text, textAlign: TextAlign.center),
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void popMeDaddy() {
|
void popMeDaddy() {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
|
@ -78,11 +62,10 @@ class _RegisterScreen extends State<RegisterScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
var colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
|
||||||
title: Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context)),
|
|
||||||
),
|
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 600),
|
constraints: const BoxConstraints(maxWidth: 600),
|
||||||
|
@ -91,35 +74,34 @@ class _RegisterScreen extends State<RegisterScreen> {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
TextFormField(
|
Icon(
|
||||||
decoration: const InputDecoration(
|
Icons.group_add,
|
||||||
labelText: 'Username',
|
size: 100,
|
||||||
),
|
color: colorScheme.primary,
|
||||||
|
),
|
||||||
|
FTextInput(
|
||||||
|
labelText: 'Username',
|
||||||
controller: usernameController,
|
controller: usernameController,
|
||||||
autofillHints: const [AutofillHints.username],
|
autofillHints: const [AutofillHints.username],
|
||||||
|
autofocus: true,
|
||||||
),
|
),
|
||||||
TextFormField(
|
FTextInput(
|
||||||
obscureText: true,
|
labelText: 'Password',
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'Password',
|
|
||||||
),
|
|
||||||
controller: passwordController,
|
controller: passwordController,
|
||||||
autofillHints: const [AutofillHints.password],
|
autofillHints: const [AutofillHints.password],
|
||||||
|
obscureText: true,
|
||||||
|
),
|
||||||
|
FTextInput(
|
||||||
|
labelText: 'Confirm password',
|
||||||
|
controller: passwordConfirmController,
|
||||||
|
autofillHints: const [AutofillHints.password],
|
||||||
|
onFieldSubmitted: (_) => _register(),
|
||||||
|
obscureText: true,
|
||||||
|
),
|
||||||
|
FButton(
|
||||||
|
labelText: 'Register account',
|
||||||
|
onPressed: _register,
|
||||||
),
|
),
|
||||||
TextFormField(
|
|
||||||
obscureText: true,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'Confirm password',
|
|
||||||
),
|
|
||||||
controller: passwordConfirmController,
|
|
||||||
autofillHints: const [AutofillHints.password],
|
|
||||||
onFieldSubmitted: (_) => _register()),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
||||||
child: FilledButton(
|
|
||||||
onPressed: _register,
|
|
||||||
child: const Text('Register'),
|
|
||||||
)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
16
pubspec.lock
16
pubspec.lock
|
@ -65,6 +65,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
flex_color_scheme:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flex_color_scheme
|
||||||
|
sha256: "32914024a4f404d90ff449f58d279191675b28e7c08824046baf06826e99d984"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.3.1"
|
||||||
|
flex_seed_scheme:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flex_seed_scheme
|
||||||
|
sha256: "29c12aba221eb8a368a119685371381f8035011d18de5ba277ad11d7dfb8657f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
|
@ -39,6 +39,7 @@ dependencies:
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
flutter_secure_storage: ^9.0.0
|
flutter_secure_storage: ^9.0.0
|
||||||
simple_barcode_scanner: ^0.1.1
|
simple_barcode_scanner: ^0.1.1
|
||||||
|
flex_color_scheme: ^7.3.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue