[UI craze] linted

This commit is contained in:
Piotr Domański 2024-03-30 14:07:10 +01:00
parent 66b88f64fe
commit ac803d3e71
22 changed files with 380 additions and 349 deletions

View file

@ -5,13 +5,12 @@ import 'package:fooder/models/meal.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class ApiClient { class ApiClient {
final String baseUrl; final String baseUrl;
String? token; String? token;
String? refreshToken; String? refreshToken;
http.Client httpClient = http.Client(); http.Client httpClient = http.Client();
final FlutterSecureStorage storage = FlutterSecureStorage(); final FlutterSecureStorage storage = const FlutterSecureStorage();
ApiClient({ ApiClient({
required this.baseUrl, required this.baseUrl,
@ -219,8 +218,9 @@ class ApiClient {
} }
Future<Map<String, dynamic>> getProductByBarcode(String barcode) async { Future<Map<String, dynamic>> getProductByBarcode(String barcode) async {
var response = var response = await get("/product/by_barcode?${Uri(queryParameters: {
await get("/product/by_barcode?${Uri(queryParameters: {"barcode": barcode}).query}"); "barcode": barcode
}).query}");
return response; return response;
} }
@ -283,8 +283,7 @@ class ApiClient {
} }
} }
Future<void> addMeal( Future<void> addMeal({required String name, required int diaryId}) async {
{required String name, required int diaryId}) async {
await post("/meal", { await post("/meal", {
"name": name, "name": name,
"diary_id": diaryId, "diary_id": diaryId,
@ -292,7 +291,9 @@ class ApiClient {
} }
Future<void> addMealFromPreset( Future<void> addMealFromPreset(
{required String name, required int diaryId, required int presetId}) async { {required String name,
required int diaryId,
required int presetId}) async {
await post("/meal/from_preset", { await post("/meal/from_preset", {
"name": name, "name": name,
"diary_id": diaryId, "diary_id": diaryId,

View file

@ -1,25 +0,0 @@
import 'package:flutter/material.dart';
class FAppBar extends StatelessWidget implements PreferredSizeWidget {
final List<Widget> actions;
const FAppBar({super.key, required this.actions});
@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,
actions: actions,
)
);
}
@override
Size get preferredSize => Size.fromHeight(56);
}

View file

@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
class FAppBar extends StatelessWidget implements PreferredSizeWidget {
final List<Widget> actions;
const FAppBar({super.key, required this.actions});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
actions: actions,
));
}
@override
Size get preferredSize => const Size.fromHeight(56);
}

View file

@ -7,7 +7,13 @@ class FButton extends StatelessWidget {
final double fontSize; final double fontSize;
final Function()? onPressed; final Function()? onPressed;
const FButton({super.key, required this.labelText, this.padding = 8, this.insidePadding = 24, this.fontSize = 20, this.onPressed}); const FButton(
{super.key,
required this.labelText,
this.padding = 8,
this.insidePadding = 24,
this.fontSize = 20,
this.onPressed});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -15,38 +21,37 @@ class FButton extends StatelessWidget {
var colorScheme = theme.colorScheme; var colorScheme = theme.colorScheme;
return GestureDetector( return GestureDetector(
onTap: onPressed, onTap: onPressed,
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(vertical: padding, horizontal: padding), padding: EdgeInsets.symmetric(vertical: padding, horizontal: padding),
child: Container( child: Container(
padding: EdgeInsets.symmetric(vertical: insidePadding), padding: EdgeInsets.symmetric(vertical: insidePadding),
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [
colorScheme.primary.withOpacity(0.5), colorScheme.primary.withOpacity(0.5),
colorScheme.secondary.withOpacity(0.5), colorScheme.secondary.withOpacity(0.5),
],
),
borderRadius: BorderRadius.circular(4),
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(0.3),
blurRadius: 5,
offset: const Offset(0, 5),
)
], ],
), ),
borderRadius: BorderRadius.circular(4), child: Center(
boxShadow: [ child: Text(
BoxShadow( labelText,
color: colorScheme.primary.withOpacity(0.3), style: theme.textTheme.labelLarge!.copyWith(
blurRadius: 5, fontWeight: FontWeight.bold,
offset: const Offset(0, 5), fontSize: fontSize,
) ),
],
),
child: Center(
child: Text(
labelText,
style: theme.textTheme.button!.copyWith(
fontWeight: FontWeight.bold,
fontSize: fontSize,
), ),
), ),
), ),
), ));
)
);
} }
} }

View file

@ -1,12 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class FDateItemWidget extends StatelessWidget { class FDateItemWidget extends StatelessWidget {
final DateTime date; final DateTime date;
final bool picked; final bool picked;
final Function(DateTime) onDatePicked; final Function(DateTime) onDatePicked;
const FDateItemWidget({super.key, required this.date, required this.onDatePicked, this.picked = false}); const FDateItemWidget(
{super.key,
required this.date,
required this.onDatePicked,
this.picked = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -28,7 +31,7 @@ class FDateItemWidget extends StatelessWidget {
onDatePicked(date); onDatePicked(date);
}, },
child: Container( child: Container(
width: picked? 100: 50, width: picked ? 100 : 50,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50), borderRadius: BorderRadius.circular(50),
border: Border.all( border: Border.all(
@ -36,7 +39,9 @@ class FDateItemWidget extends StatelessWidget {
color: Colors.transparent, color: Colors.transparent,
width: 2, width: 2,
), ),
color: picked ? colorScheme.onPrimary.withOpacity(0.25) : Colors.transparent, color: picked
? colorScheme.onPrimary.withOpacity(0.25)
: Colors.transparent,
), ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -45,7 +50,7 @@ class FDateItemWidget extends StatelessWidget {
dayOfTheWeekMap[date.weekday]!, dayOfTheWeekMap[date.weekday]!,
style: TextStyle( style: TextStyle(
color: colorScheme.onPrimary, color: colorScheme.onPrimary,
fontSize: picked ? 24: 12, fontSize: picked ? 24 : 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
@ -53,7 +58,7 @@ class FDateItemWidget extends StatelessWidget {
'${date.day}.${date.month}', '${date.day}.${date.month}',
style: TextStyle( style: TextStyle(
color: colorScheme.onPrimary, color: colorScheme.onPrimary,
fontSize: picked ? 24: 12, fontSize: picked ? 24 : 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
@ -68,7 +73,8 @@ class FDatePickerWidget extends StatefulWidget {
final DateTime date; final DateTime date;
final Function(DateTime) onDatePicked; final Function(DateTime) onDatePicked;
const FDatePickerWidget({super.key, required this.date, required this.onDatePicked}); const FDatePickerWidget(
{super.key, required this.date, required this.onDatePicked});
@override @override
State<FDatePickerWidget> createState() => _FDatePickerWidgetState(); State<FDatePickerWidget> createState() => _FDatePickerWidgetState();
@ -106,13 +112,18 @@ class _FDatePickerWidgetState extends State<FDatePickerWidget> {
padding: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
shrinkWrap: true, shrinkWrap: true,
children: <Widget>[ children: <Widget>[
FDateItemWidget(date: date.add(Duration(days: -3)), onDatePicked: onDatePicked), FDateItemWidget(
FDateItemWidget(date: date.add(Duration(days: -2)), onDatePicked: onDatePicked), date: date.add(const Duration(days: -2)),
FDateItemWidget(date: date.add(Duration(days: -1)), onDatePicked: onDatePicked), onDatePicked: onDatePicked),
FDateItemWidget(date: date, onDatePicked: onDatePicked, picked: true), FDateItemWidget(
FDateItemWidget(date: date.add(Duration(days: 1)), onDatePicked: onDatePicked), date: date.add(const Duration(days: -1)),
FDateItemWidget(date: date.add(Duration(days: 2)), onDatePicked: onDatePicked), onDatePicked: onDatePicked),
Container( FDateItemWidget(
date: date, onDatePicked: onDatePicked, picked: true),
FDateItemWidget(
date: date.add(const Duration(days: 1)),
onDatePicked: onDatePicked),
SizedBox(
width: 50, width: 50,
child: IconButton( child: IconButton(
icon: Icon( icon: Icon(
@ -125,8 +136,8 @@ class _FDatePickerWidgetState extends State<FDatePickerWidget> {
showDatePicker( showDatePicker(
context: context, context: context,
initialDate: date, initialDate: date,
firstDate: date.add(Duration(days: -365)), firstDate: date.add(const Duration(days: -365)),
lastDate: date.add(Duration(days: 365)), lastDate: date.add(const Duration(days: 365)),
).then((value) { ).then((value) {
if (value != null) { if (value != null) {
onDatePicked(value); onDatePicked(value);

View file

@ -1,64 +0,0 @@
import 'package:flutter/material.dart';
import 'package:blur/blur.dart';
class FNavBar extends StatelessWidget {
final List<Widget> children;
final double height;
const FNavBar ({super.key, required this.children, this.height = 56});
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
return SafeArea(
child: Padding(
padding: EdgeInsets.all(12),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(0.3),
blurRadius: 5,
offset: const Offset(0, 5),
)
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Stack(
children: [
Blur(
blur: 10,
blurColor: colorScheme.primary.withOpacity(0.1),
child: Container(
width: MediaQuery.of(context).size.width,
height: height * children.length,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
colorScheme.primary.withOpacity(0.1),
colorScheme.secondary.withOpacity(0.1),
],
),
),
),
),
Container(
width: MediaQuery.of(context).size.width,
height: height * children.length,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
),
],
)
),
),
),
);
}
}

View file

@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:blur/blur.dart';
class FNavBar extends StatelessWidget {
static const maxWidth = 920.0;
final List<Widget> children;
final double height;
const FNavBar({super.key, required this.children, this.height = 56});
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
var widthAvail = MediaQuery.of(context).size.width;
// var width = widthAvail > maxWidth ? maxWidth : widthAvail;
return SafeArea(
child: Padding(
padding: const EdgeInsets.all(12),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(0.3),
blurRadius: 5,
offset: const Offset(0, 5),
)
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Stack(
children: [
Blur(
blur: 10,
blurColor: colorScheme.primary.withOpacity(0.1),
child: Container(
width: widthAvail,
height: height * children.length,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
colorScheme.primary.withOpacity(0.1),
colorScheme.secondary.withOpacity(0.1),
],
),
),
),
),
SizedBox(
width: widthAvail,
height: height * children.length,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
),
],
)),
),
),
);
}
}

View file

@ -1,13 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:blur/blur.dart'; import 'package:blur/blur.dart';
class ClipShadowPath extends StatelessWidget { class ClipShadowPath extends StatelessWidget {
final Shadow shadow; final Shadow shadow;
final CustomClipper<Path> clipper; final CustomClipper<Path> clipper;
final Widget child; final Widget child;
ClipShadowPath({ const ClipShadowPath({
super.key,
required this.shadow, required this.shadow,
required this.clipper, required this.clipper,
required this.child, required this.child,
@ -17,10 +17,10 @@ class ClipShadowPath extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CustomPaint( return CustomPaint(
painter: _ClipShadowShadowPainter( painter: _ClipShadowShadowPainter(
clipper: this.clipper, clipper: clipper,
shadow: this.shadow, shadow: shadow,
), ),
child: ClipPath(child: child, clipper: this.clipper), child: ClipPath(clipper: clipper, child: child),
); );
} }
} }
@ -47,7 +47,7 @@ class _ClipShadowShadowPainter extends CustomPainter {
class BackgroundWave extends StatelessWidget { class BackgroundWave extends StatelessWidget {
final double height; final double height;
const BackgroundWave({Key? key, required this.height}) : super(key: key); const BackgroundWave({super.key, required this.height});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -94,21 +94,30 @@ class BackgroundWaveClipper extends CustomClipper<Path> {
var lastCurve = Offset(40, size.height - 20); var lastCurve = Offset(40, size.height - 20);
path.quadraticBezierTo( path.quadraticBezierTo(
firstCurve.dx, firstCurve.dy, lastCurve.dx, lastCurve.dy, firstCurve.dx,
firstCurve.dy,
lastCurve.dx,
lastCurve.dy,
); );
firstCurve = Offset(0, size.height - 20); firstCurve = Offset(0, size.height - 20);
lastCurve = Offset(size.width - 40, size.height - 20); lastCurve = Offset(size.width - 40, size.height - 20);
path.quadraticBezierTo( path.quadraticBezierTo(
firstCurve.dx, firstCurve.dy, lastCurve.dx, lastCurve.dy, firstCurve.dx,
firstCurve.dy,
lastCurve.dx,
lastCurve.dy,
); );
firstCurve = Offset(size.width, size.height - 20); firstCurve = Offset(size.width, size.height - 20);
lastCurve = Offset(size.width, size.height); lastCurve = Offset(size.width, size.height);
path.quadraticBezierTo( path.quadraticBezierTo(
firstCurve.dx, firstCurve.dy, lastCurve.dx, lastCurve.dy, firstCurve.dx,
firstCurve.dy,
lastCurve.dx,
lastCurve.dy,
); );
path.lineTo(size.width, 0.0); path.lineTo(size.width, 0.0);
@ -126,8 +135,10 @@ class FSliverAppBar extends SliverPersistentHeaderDelegate {
const FSliverAppBar({required this.child}); const FSliverAppBar({required this.child});
@override @override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { Widget build(
var adjustedShrinkOffset = shrinkOffset > minExtent ? minExtent : shrinkOffset; BuildContext context, double shrinkOffset, bool overlapsContent) {
var adjustedShrinkOffset =
shrinkOffset > minExtent ? minExtent : shrinkOffset;
double offset = (minExtent - adjustedShrinkOffset); double offset = (minExtent - adjustedShrinkOffset);
if (offset < 4) { if (offset < 4) {
@ -141,9 +152,9 @@ class FSliverAppBar extends SliverPersistentHeaderDelegate {
), ),
Positioned( Positioned(
top: offset, top: offset,
child: child,
left: 16, left: 16,
right: 16, right: 16,
child: child,
) )
], ],
); );

View file

@ -9,7 +9,15 @@ class FTextInput extends StatelessWidget {
final bool obscureText; final bool obscureText;
final Function(String)? onFieldSubmitted; 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}); const FTextInput(
{super.key,
required this.labelText,
this.padding = 8,
required this.controller,
this.autofillHints,
this.autofocus = false,
this.onFieldSubmitted,
this.obscureText = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View file

@ -7,8 +7,6 @@ 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;
@ -58,6 +56,7 @@ class _AddEntryScreen extends BasedState<AddEntryScreen> {
}); });
} }
@override
void showError(String message) { void showError(String message) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
@ -99,7 +98,6 @@ class _AddEntryScreen extends BasedState<AddEntryScreen> {
popMeDaddy(); popMeDaddy();
} }
Future<void> _findProductByBarCode() async { Future<void> _findProductByBarCode() async {
var res = await Navigator.push( var res = await Navigator.push(
context, context,
@ -110,8 +108,7 @@ class _AddEntryScreen extends BasedState<AddEntryScreen> {
if (res is String) { if (res is String) {
try { try {
var productMap = var productMap = await widget.apiClient.getProductByBarcode(res);
await widget.apiClient.getProductByBarcode(res);
var product = Product.fromJson(productMap); var product = Product.fromJson(productMap);

View file

@ -43,7 +43,8 @@ class _AddProductScreen extends State<AddProductScreen> {
); );
} }
Future<double?> _parseDouble(String text, String name, {bool silent = false}) async { Future<double?> _parseDouble(String text, String name,
{bool silent = false}) async {
try { try {
return double.parse(text.replaceAll(",", ".")); return double.parse(text.replaceAll(",", "."));
} catch (e) { } catch (e) {
@ -58,7 +59,8 @@ class _AddProductScreen extends State<AddProductScreen> {
var carb = await _parseDouble(carbController.text, "Carbs"); var carb = await _parseDouble(carbController.text, "Carbs");
var fat = await _parseDouble(fatController.text, "Fat"); var fat = await _parseDouble(fatController.text, "Fat");
var protein = await _parseDouble(proteinController.text, "Protein"); var protein = await _parseDouble(proteinController.text, "Protein");
var fiber = await _parseDouble(fiberController.text, "Fiber", silent: true) ?? 0; var fiber =
await _parseDouble(fiberController.text, "Fiber", silent: true) ?? 0;
if (carb == null || fat == null || protein == null) { if (carb == null || fat == null || protein == null) {
return; return;

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fooder/client.dart'; import 'package:fooder/client.dart';
import 'package:fooder/components/appBar.dart'; import 'package:fooder/components/app_bar.dart';
import 'package:fooder/components/navigationBar.dart'; import 'package:fooder/components/navigation_bar.dart';
import 'package:fooder/screens/login.dart'; import 'package:fooder/screens/login.dart';
import 'package:fooder/screens/main.dart'; import 'package:fooder/screens/main.dart';
@ -69,21 +69,21 @@ abstract class BasedState<T extends BasedScreen> extends State<T> {
Icons.dinner_dining, Icons.dinner_dining,
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
), ),
onPressed: () => null, onPressed: () {},
), ),
IconButton( IconButton(
icon: Icon( icon: Icon(
Icons.lunch_dining, Icons.lunch_dining,
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
), ),
onPressed: () => null, onPressed: () {},
), ),
IconButton( IconButton(
icon: Icon( icon: Icon(
Icons.person, Icons.person,
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
), ),
onPressed: () => null, onPressed: () {},
), ),
], ],
), ),
@ -98,9 +98,9 @@ abstract class BasedState<T extends BasedScreen> extends State<T> {
message, message,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyLarge!.copyWith( style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onError, color: Theme.of(context).colorScheme.onError,
), ),
), ),
backgroundColor: Theme.of(context).colorScheme.error.withOpacity(0.8), backgroundColor: Theme.of(context).colorScheme.error.withOpacity(0.8),
), ),
@ -114,9 +114,9 @@ abstract class BasedState<T extends BasedScreen> extends State<T> {
text, text,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyLarge!.copyWith( style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
), ),
), ),
backgroundColor: Theme.of(context).colorScheme.primary.withOpacity(0.8), backgroundColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
), ),

View file

@ -106,8 +106,7 @@ class _EditEntryScreen extends State<EditEntryScreen> {
if (res is String) { if (res is String) {
try { try {
var productMap = var productMap = await widget.apiClient.getProductByBarcode(res);
await widget.apiClient.getProductByBarcode(res);
var product = Product.fromJson(productMap); var product = Product.fromJson(productMap);

View file

@ -1,14 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fooder/screens/based.dart'; import 'package:fooder/screens/based.dart';
import 'package:fooder/screens/add_entry.dart'; import 'package:fooder/screens/add_entry.dart';
import 'package:fooder/screens/add_meal.dart';
import 'package:fooder/models/diary.dart'; import 'package:fooder/models/diary.dart';
import 'package:fooder/widgets/diary.dart';
import 'package:fooder/widgets/summary.dart'; import 'package:fooder/widgets/summary.dart';
import 'package:fooder/widgets/meal.dart'; import 'package:fooder/widgets/meal.dart';
import 'package:fooder/widgets/macroEntry.dart';
import 'package:fooder/components/sliver.dart'; import 'package:fooder/components/sliver.dart';
import 'package:fooder/components/datePicker.dart'; import 'package:fooder/components/date_picker.dart';
import 'package:blur/blur.dart'; import 'package:blur/blur.dart';
class MainScreen extends BasedScreen { class MainScreen extends BasedScreen {
@ -60,54 +57,54 @@ class _MainScreen extends BasedState<MainScreen> {
var colorScheme = theme.colorScheme; var colorScheme = theme.colorScheme;
return Container( return Container(
height: 64, height: 64,
width: 64, width: 64,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32), borderRadius: BorderRadius.circular(32),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: colorScheme.primary.withOpacity(0.3), color: colorScheme.primary.withOpacity(0.3),
blurRadius: 5, blurRadius: 5,
offset: const Offset(0, 5), offset: const Offset(0, 5),
) )
], ],
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(32), borderRadius: BorderRadius.circular(32),
child: Stack( child: Stack(
children: [ children: [
Blur( Blur(
blur: 10, blur: 10,
blurColor: colorScheme.primary.withOpacity(0.1), blurColor: colorScheme.primary.withOpacity(0.1),
child: Container( child: Container(
height: 64,
width: 64,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
colorScheme.primary.withOpacity(0.1),
colorScheme.secondary.withOpacity(0.1),
],
),
),
),
),
Container(
height: 64, height: 64,
width: 64, width: 64,
child: FloatingActionButton( decoration: BoxDecoration(
elevation: 0, gradient: LinearGradient(
onPressed: _addEntry, colors: [
backgroundColor: Colors.transparent, colorScheme.primary.withOpacity(0.1),
child: Icon( colorScheme.secondary.withOpacity(0.1),
Icons.library_add, ],
color: colorScheme.onPrimary,
), ),
), ),
), ),
], ),
), SizedBox(
height: 64,
width: 64,
child: FloatingActionButton(
elevation: 0,
onPressed: _addEntry,
backgroundColor: Colors.transparent,
child: Icon(
Icons.library_add,
color: colorScheme.onPrimary,
),
),
),
],
), ),
),
); );
} }
@ -116,37 +113,35 @@ class _MainScreen extends BasedState<MainScreen> {
Widget content; Widget content;
if (diary != null) { if (diary != null) {
content = CustomScrollView( content = CustomScrollView(slivers: <Widget>[
slivers: <Widget>[ SliverPersistentHeader(
SliverPersistentHeader( delegate: FSliverAppBar(
delegate: FSliverAppBar(child: FDatePickerWidget(date: date, onDatePicked: _pickDate)), child: FDatePickerWidget(date: date, onDatePicked: _pickDate)),
pinned: true, pinned: true,
), ),
SliverList( SliverList(
delegate: SliverChildListDelegate( delegate: SliverChildListDelegate(
[ [
SummaryWidget( SummaryWidget(
diary: diary!, diary: diary!,
apiClient: widget.apiClient,
refreshParent: _asyncInitState,
),
for (var (i, meal) in diary!.meals.indexed)
MealWidget(
meal: meal,
apiClient: widget.apiClient, apiClient: widget.apiClient,
refreshParent: _asyncInitState, refreshParent: _asyncInitState,
initiallyExpanded: i == 0,
), ),
for (var (i, meal) in diary!.meals.indexed) ],
MealWidget(
meal: meal,
apiClient: widget.apiClient,
refreshParent: _asyncInitState,
initiallyExpanded: i == 0,
),
],
),
), ),
] ),
); ]);
} else { } else {
content = const Center(child: const CircularProgressIndicator()); content = const Center(child: CircularProgressIndicator());
} }
return Scaffold( return Scaffold(
body: content, body: content,
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,

View file

@ -7,7 +7,11 @@ class MealScreen extends BasedScreen {
final Meal meal; final Meal meal;
final Function() refresh; final Function() refresh;
const MealScreen({super.key, required super.apiClient, required this.refresh, required this.meal}); const MealScreen(
{super.key,
required super.apiClient,
required this.refresh,
required this.meal});
@override @override
State<MealScreen> createState() => _AddMealScreen(); State<MealScreen> createState() => _AddMealScreen();

View file

@ -3,13 +3,10 @@ import 'package:fooder/models/entry.dart';
import 'package:fooder/widgets/macroEntry.dart'; import 'package:fooder/widgets/macroEntry.dart';
import 'dart:core'; import 'dart:core';
class EntryHeader extends StatelessWidget { class EntryHeader extends StatelessWidget {
final Entry entry; final Entry entry;
const EntryHeader( const EntryHeader({super.key, required this.entry});
{super.key,
required this.entry});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -20,19 +17,19 @@ class EntryHeader extends StatelessWidget {
child: Text( child: Text(
entry.product.name, entry.product.name,
style: Theme.of(context).textTheme.bodyLarge!.copyWith( style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
), ),
Spacer(), const Spacer(),
Padding( Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Text( child: Text(
entry.grams.toStringAsFixed(0) + " g", "${entry.grams.toStringAsFixed(0)} g",
style: Theme.of(context).textTheme.bodyText2!.copyWith( style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
), ),
), ),
), ),
], ],

View file

@ -11,7 +11,7 @@ class MacroWidget extends StatelessWidget {
final Widget? child; final Widget? child;
const MacroWidget({ const MacroWidget({
Key? key, super.key,
this.calories, this.calories,
this.amount, this.amount,
this.child, this.child,
@ -20,7 +20,7 @@ class MacroWidget extends StatelessWidget {
required this.carb, required this.carb,
required this.fat, required this.fat,
required this.style, required this.style,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View file

@ -1,22 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:core'; import 'dart:core';
class MacroHeaderWidget extends StatelessWidget { class MacroHeaderWidget extends StatelessWidget {
static final double PAD_Y = 4; static const double PAD_Y = 4;
static final double PAD_X = 8; static const double PAD_X = 8;
final bool? fiber; final bool? fiber;
final bool? calories; final bool? calories;
const MacroHeaderWidget( const MacroHeaderWidget({
{ super.key,
super.key, this.fiber = false,
this.fiber = false, this.calories = false,
this.calories = false, });
}
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -43,16 +39,16 @@ class MacroHeaderWidget extends StatelessWidget {
for (var element in elements) { for (var element in elements) {
children.add( children.add(
Padding( Padding(
padding: EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 2, horizontal: 2,
), ),
child: SizedBox( child: SizedBox(
width: 55, width: 55,
child: Text( child: Text(
element, element,
style: Theme.of(context).textTheme.bodyText1!.copyWith( style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
@ -60,10 +56,10 @@ class MacroHeaderWidget extends StatelessWidget {
); );
} }
children.add(Spacer()); children.add(const Spacer());
return Padding( return Padding(
padding: EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: PAD_Y, vertical: PAD_Y,
horizontal: PAD_X, horizontal: PAD_X,
), ),
@ -75,10 +71,9 @@ class MacroHeaderWidget extends StatelessWidget {
} }
} }
class MacroEntryWidget extends StatelessWidget { class MacroEntryWidget extends StatelessWidget {
static final double PAD_Y = 4; static const double PAD_Y = 4;
static final double PAD_X = 8; static const double PAD_X = 8;
final double protein; final double protein;
final double carb; final double carb;
@ -86,35 +81,32 @@ class MacroEntryWidget extends StatelessWidget {
final double? fiber; final double? fiber;
final double? calories; final double? calories;
const MacroEntryWidget( const MacroEntryWidget({
{ super.key,
super.key, required this.protein,
required this.protein, required this.carb,
required this.carb, required this.fat,
required this.fat, this.fiber,
this.fiber, this.calories,
this.calories, });
}
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var elements = <String>[ var elements = <String>[
"${carb.toStringAsFixed(1)}", (carb.toStringAsFixed(1)),
"${fat.toStringAsFixed(1)}", (fat.toStringAsFixed(1)),
"${protein.toStringAsFixed(1)}", (protein.toStringAsFixed(1)),
]; ];
if (fiber != null) { if (fiber != null) {
elements.add( elements.add(
"${fiber!.toStringAsFixed(1)}", fiber!.toStringAsFixed(1),
); );
} }
if (calories != null) { if (calories != null) {
elements.add( elements.add(
"${calories!.toStringAsFixed(0)}", calories!.toStringAsFixed(0),
); );
} }
@ -123,16 +115,16 @@ class MacroEntryWidget extends StatelessWidget {
for (var element in elements) { for (var element in elements) {
children.add( children.add(
Padding( Padding(
padding: EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 2, horizontal: 2,
), ),
child: SizedBox( child: SizedBox(
width: 55, width: 55,
child: Text( child: Text(
element, element,
style: Theme.of(context).textTheme.bodyText1!.copyWith( style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
@ -140,10 +132,10 @@ class MacroEntryWidget extends StatelessWidget {
); );
} }
children.add(Spacer()); children.add(const Spacer());
return Padding( return Padding(
padding: EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: PAD_Y, vertical: PAD_Y,
horizontal: PAD_X, horizontal: PAD_X,
), ),

View file

@ -7,13 +7,10 @@ import 'package:fooder/screens/meal.dart';
import 'package:fooder/client.dart'; import 'package:fooder/client.dart';
import 'dart:core'; import 'dart:core';
class MealHeader extends StatelessWidget { class MealHeader extends StatelessWidget {
final Meal meal; final Meal meal;
const MealHeader( const MealHeader({super.key, required this.meal});
{super.key,
required this.meal});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -24,9 +21,9 @@ class MealHeader extends StatelessWidget {
child: Text( child: Text(
meal.name, meal.name,
style: Theme.of(context).textTheme.headlineSmall!.copyWith( style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
), ),
], ],
@ -34,24 +31,21 @@ class MealHeader extends StatelessWidget {
} }
} }
class MealWidget extends StatelessWidget { class MealWidget extends StatelessWidget {
static final MAX_WIDTH = 920.0; static const maxWidth = 920.0;
final Meal meal; final Meal meal;
final ApiClient apiClient; final ApiClient apiClient;
final Function() refreshParent; final Function() refreshParent;
final bool initiallyExpanded; final bool initiallyExpanded;
const MealWidget( const MealWidget({
{ super.key,
super.key, required this.meal,
required this.meal, required this.apiClient,
required this.apiClient, required this.refreshParent,
required this.refreshParent, required this.initiallyExpanded,
required this.initiallyExpanded, });
}
);
Future<void> _editMeal(context) async { Future<void> _editMeal(context) async {
await Navigator.push( await Navigator.push(
@ -83,8 +77,8 @@ class MealWidget extends StatelessWidget {
var theme = Theme.of(context); var theme = Theme.of(context);
var colorScheme = theme.colorScheme; var colorScheme = theme.colorScheme;
var width_avail = MediaQuery.of(context).size.width; var widthAvail = MediaQuery.of(context).size.width;
var width = width_avail > MAX_WIDTH ? MAX_WIDTH : width_avail; var width = widthAvail > maxWidth ? maxWidth : widthAvail;
return Center( return Center(
child: Padding( child: Padding(
@ -119,7 +113,7 @@ class MealWidget extends StatelessWidget {
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
MealHeader(meal: meal), MealHeader(meal: meal),
MacroHeaderWidget( const MacroHeaderWidget(
calories: true, calories: true,
), ),
MacroEntryWidget( MacroEntryWidget(
@ -137,7 +131,9 @@ class MealWidget extends StatelessWidget {
title: EntryWidget( title: EntryWidget(
entry: entry, entry: entry,
), ),
tileColor: i % 2 == 0 ? colorScheme.secondary.withOpacity(0.1): Colors.transparent, tileColor: i % 2 == 0
? colorScheme.secondary.withOpacity(0.1)
: Colors.transparent,
onTap: () => _editEntry(context, entry), onTap: () => _editEntry(context, entry),
) )
], ],

View file

@ -5,15 +5,11 @@ import 'package:fooder/screens/add_meal.dart';
import 'package:fooder/client.dart'; import 'package:fooder/client.dart';
import 'dart:core'; import 'dart:core';
class SummaryHeader extends StatelessWidget { class SummaryHeader extends StatelessWidget {
final Diary diary; final Diary diary;
final Function addMeal; final Function addMeal;
const SummaryHeader( const SummaryHeader({super.key, required this.addMeal, required this.diary});
{super.key,
required this.addMeal,
required this.diary});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -24,16 +20,16 @@ class SummaryHeader extends StatelessWidget {
child: Text( child: Text(
"Summary", "Summary",
style: Theme.of(context).textTheme.headlineSmall!.copyWith( style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
), ),
Spacer(), const Spacer(),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.symmetric(horizontal: 8),
child: IconButton( child: IconButton(
icon: Icon(Icons.playlist_add_rounded), icon: const Icon(Icons.playlist_add_rounded),
iconSize: 32, iconSize: 32,
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
onPressed: () => addMeal(context), onPressed: () => addMeal(context),
@ -44,9 +40,8 @@ class SummaryHeader extends StatelessWidget {
} }
} }
class SummaryWidget extends StatelessWidget { class SummaryWidget extends StatelessWidget {
static final MAX_WIDTH = 920.0; static const maxWidth = 920.0;
final Diary diary; final Diary diary;
final ApiClient apiClient; final ApiClient apiClient;
@ -75,8 +70,8 @@ class SummaryWidget extends StatelessWidget {
var theme = Theme.of(context); var theme = Theme.of(context);
var colorScheme = theme.colorScheme; var colorScheme = theme.colorScheme;
var width_avail = MediaQuery.of(context).size.width; var widthAvail = MediaQuery.of(context).size.width;
var width = width_avail > MAX_WIDTH ? MAX_WIDTH : width_avail; var width = widthAvail > maxWidth ? maxWidth : widthAvail;
return Center( return Center(
child: Padding( child: Padding(
@ -100,11 +95,12 @@ class SummaryWidget extends StatelessWidget {
), ),
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24), padding:
const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
SummaryHeader(diary: diary, addMeal: _addMeal), SummaryHeader(diary: diary, addMeal: _addMeal),
MacroHeaderWidget( const MacroHeaderWidget(
calories: true, calories: true,
), ),
MacroEntryWidget( MacroEntryWidget(

View file

@ -65,6 +65,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.6" version: "1.0.6"
fading_edge_scrollview:
dependency: transitive
description:
name: fading_edge_scrollview
sha256: c25c2231652ce774cc31824d0112f11f653881f43d7f5302c05af11942052031
url: "https://pub.dev"
source: hosted
version: "3.0.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -114,10 +122,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
@ -256,6 +264,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.0"
marquee:
dependency: "direct main"
description:
name: marquee
sha256: "4b5243d2804373bdc25fc93d42c3b402d6ec1f4ee8d0bb72276edd04ae7addb8"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -340,10 +356,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: permission_handler name: permission_handler
sha256: "74e962b7fad7ff75959161bb2c0ad8fe7f2568ee82621c9c2660b751146bfe44" sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.3.0" version: "11.3.1"
permission_handler_android: permission_handler_android:
dependency: transitive dependency: transitive
description: description:
@ -356,10 +372,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_apple name: permission_handler_apple
sha256: "92861b0f0c2443dd8898398c2baa4f1ae925109b5909ae4a17d0108a6a788932" sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.4.2" version: "9.4.4"
permission_handler_html: permission_handler_html:
dependency: transitive dependency: transitive
description: description:
@ -372,10 +388,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_platform_interface name: permission_handler_platform_interface
sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78" sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2.0" version: "4.2.1"
permission_handler_windows: permission_handler_windows:
dependency: transitive dependency: transitive
description: description:
@ -505,10 +521,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480" sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.3.0" version: "5.4.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View file

@ -42,6 +42,7 @@ dependencies:
google_fonts: ^6.2.1 google_fonts: ^6.2.1
flex_color_scheme: ^7.3.1 flex_color_scheme: ^7.3.1
blur: ^3.1.0 blur: ^3.1.0
marquee: ^2.2.3
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: