diff --git a/lib/routes.dart b/lib/routes.dart index 8a86ea636..e84624fb4 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -50,31 +50,28 @@ final List routes = [ ), routes: [ GoRoute( - path: 'sales/order/:pk/pay', - name: 'sales-order-pay', - pageBuilder: (context, state) { - return CustomTransitionPage( - barrierColor: Colors.black54, - opaque: false, - transitionDuration: const Duration(milliseconds: 150), - transitionsBuilder: ( - context, - animation, - secondaryAnimation, - child, - ) { - return FadeTransition( - opacity: CurvedAnimation( - parent: animation, - curve: Curves.easeOut, - ), - child: child, - ); - }, - child: SalesOrderDialog(pk: state.params['pk']!), - ); - }, - ), + path: 'sales/order/:pk/pay', + name: 'sales-order-pay', + pageBuilder: (context, state) => CustomTransitionPage( + barrierColor: Colors.black54, + opaque: false, + transitionDuration: const Duration(milliseconds: 150), + transitionsBuilder: ( + context, + animation, + secondaryAnimation, + child, + ) { + return FadeTransition( + opacity: CurvedAnimation( + parent: animation, + curve: Curves.easeOut, + ), + child: child, + ); + }, + child: SalesOrderDialog(pk: state.params['pk']!), + )), ]), GoRoute( path: '/events', @@ -373,15 +370,13 @@ final List routes = [ GoRoute( path: '/pizzas', name: 'food', - pageBuilder: (context, state) { - return MaterialPage( - key: state.pageKey, - child: FoodScreen( - pk: (state.extra as Event?)?.foodEvent, - event: state.extra as Event?, - ), - ); - }, + pageBuilder: (context, state) => MaterialPage( + key: state.pageKey, + child: FoodScreen( + pk: (state.extra as Event?)?.foodEvent, + event: state.extra as Event?, + ), + ), routes: [ GoRoute( path: 'admin', diff --git a/lib/tosti/tosti_shift_screen.dart b/lib/tosti/tosti_shift_screen.dart index 870a37867..3f3cc9ba1 100644 --- a/lib/tosti/tosti_shift_screen.dart +++ b/lib/tosti/tosti_shift_screen.dart @@ -175,7 +175,7 @@ class TostiShiftScreen extends StatelessWidget { return RefreshIndicator( onRefresh: () => cubit.load(id), - child: CustomScrollView( + child: SafeCustomScrollView( slivers: [header, orderList], ), ); diff --git a/lib/ui/screens/albums_screen.dart b/lib/ui/screens/albums_screen.dart index 0b5661af5..8ecf873e2 100644 --- a/lib/ui/screens/albums_screen.dart +++ b/lib/ui/screens/albums_screen.dart @@ -194,31 +194,28 @@ class AlbumListScrollView extends StatelessWidget { Widget build(BuildContext context) { return Scrollbar( controller: controller, - child: CustomScrollView( + child: SafeCustomScrollView( controller: controller, physics: const RangeMaintainingScrollPhysics( parent: AlwaysScrollableScrollPhysics(), ), slivers: [ - SliverPadding( - padding: const EdgeInsets.all(8), - sliver: SliverGrid( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - mainAxisSpacing: 8, - crossAxisSpacing: 8, - ), - delegate: SliverChildBuilderDelegate( - (context, index) => AlbumTile( - album: listState.results[index], - ), - childCount: listState.results.length, + SliverGrid( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + ), + delegate: SliverChildBuilderDelegate( + (context, index) => AlbumTile( + album: listState.results[index], ), + childCount: listState.results.length, ), ), if (listState.isLoadingMore) const SliverPadding( - padding: EdgeInsets.all(8), + padding: EdgeInsets.only(top: 8), sliver: SliverList( delegate: SliverChildListDelegate.fixed([ Center( diff --git a/lib/ui/screens/calendar_screen.dart b/lib/ui/screens/calendar_screen.dart index b66d0255b..871b761d6 100644 --- a/lib/ui/screens/calendar_screen.dart +++ b/lib/ui/screens/calendar_screen.dart @@ -234,54 +234,52 @@ class CalendarScrollView extends StatelessWidget { return Scrollbar( controller: controller, - child: CustomScrollView( + child: SafeCustomScrollView( controller: controller, physics: const RangeMaintainingScrollPhysics( parent: AlwaysScrollableScrollPhysics(), ), + padding: const EdgeInsets.all(8), slivers: [ - SliverPadding( - padding: const EdgeInsets.all(12), - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - final month = months[index]; - final events = monthGroupedEvents[month]!; - - final dayGroupedEvents = _groupByDay(events); - final days = dayGroupedEvents.keys.toList(); - - return StickyHeader( - header: SizedBox( - width: double.infinity, - child: Material( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Text( - month.year == DateTime.now().year - ? monthFormatter - .format(month.toLocal()) - .toUpperCase() - : monthYearFormatter - .format(month.toLocal()) - .toUpperCase(), - style: Theme.of(context).textTheme.titleMedium, - ), + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final month = months[index]; + final events = monthGroupedEvents[month]!; + + final dayGroupedEvents = _groupByDay(events); + final days = dayGroupedEvents.keys.toList(); + + return StickyHeader( + header: SizedBox( + width: double.infinity, + child: Material( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Text( + month.year == DateTime.now().year + ? monthFormatter + .format(month.toLocal()) + .toUpperCase() + : monthYearFormatter + .format(month.toLocal()) + .toUpperCase(), + style: Theme.of(context).textTheme.titleMedium, ), ), ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 8), - for (final day in days) - _DayCard(day: day, events: dayGroupedEvents[day]!), - ], - ), - ); - }, - childCount: monthGroupedEvents.length, - ), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 8), + for (final day in days) + _DayCard(day: day, events: dayGroupedEvents[day]!), + ], + ), + ); + }, + childCount: monthGroupedEvents.length, ), ), if (calendarState.isLoadingMore) diff --git a/lib/ui/screens/event_screen.dart b/lib/ui/screens/event_screen.dart index d7635d17c..e5c2dbeb6 100644 --- a/lib/ui/screens/event_screen.dart +++ b/lib/ui/screens/event_screen.dart @@ -898,24 +898,22 @@ class _EventScreenState extends State { actions: [_makeShareEventButton(widget.pk)], ), body: RefreshIndicator( - onRefresh: () async { - // Await only the event info. - _registrationsCubit.load(); - await _eventCubit.load(); - }, - child: ErrorScrollView(state.message!), - ), + onRefresh: () async { + // Await only the event info. + _registrationsCubit.load(); + await _eventCubit.load(); + }, + child: ErrorScrollView(state.message!)), ); } else if (state is LoadingState && state is! ResultState && widget.event == null) { return Scaffold( - appBar: ThaliaAppBar( - title: const Text('EVENT'), - actions: [_makeShareEventButton(widget.pk)], - ), - body: const Center(child: CircularProgressIndicator()), - ); + appBar: ThaliaAppBar( + title: const Text('EVENT'), + actions: [_makeShareEventButton(widget.pk)], + ), + body: const Center(child: CircularProgressIndicator())); } else { final event = (state.result ?? widget.event)!; return Scaffold( @@ -946,7 +944,7 @@ class _EventScreenState extends State { builder: (context, listState) { return Scrollbar( controller: _controller, - child: CustomScrollView( + child: SafeCustomScrollView( controller: _controller, key: const PageStorageKey('event'), slivers: [ diff --git a/lib/ui/screens/food_admin_screen.dart b/lib/ui/screens/food_admin_screen.dart index 47fa49693..a35d2abc7 100644 --- a/lib/ui/screens/food_admin_screen.dart +++ b/lib/ui/screens/food_admin_screen.dart @@ -87,6 +87,7 @@ class _FoodAdminScreenState extends State { } } +//TODO: This does not carry any state? class _OrderTile extends StatefulWidget { final AdminFoodOrder order; diff --git a/lib/ui/screens/group_screen.dart b/lib/ui/screens/group_screen.dart index d625487f7..d2b8c7191 100644 --- a/lib/ui/screens/group_screen.dart +++ b/lib/ui/screens/group_screen.dart @@ -98,7 +98,7 @@ class _Page extends StatelessWidget { body: RefreshIndicator( onRefresh: () => cubit.load(), child: Scrollbar( - child: CustomScrollView( + child: SafeCustomScrollView( key: const PageStorageKey('group'), slivers: [ SliverToBoxAdapter( @@ -125,7 +125,7 @@ class _Page extends StatelessWidget { body: RefreshIndicator( onRefresh: () => cubit.load(), child: Scrollbar( - child: CustomScrollView( + child: SafeCustomScrollView( key: const PageStorageKey('group'), slivers: [ SliverToBoxAdapter( diff --git a/lib/ui/screens/groups_screen.dart b/lib/ui/screens/groups_screen.dart index 535122951..eff4c3526 100644 --- a/lib/ui/screens/groups_screen.dart +++ b/lib/ui/screens/groups_screen.dart @@ -158,7 +158,7 @@ class GroupListScrollView extends StatelessWidget { @override Widget build(BuildContext context) { return Scrollbar( - child: CustomScrollView( + child: SafeCustomScrollView( physics: const RangeMaintainingScrollPhysics( parent: AlwaysScrollableScrollPhysics(), ), @@ -166,25 +166,22 @@ class GroupListScrollView extends StatelessWidget { if (activeBoard != null) SliverToBoxAdapter( child: Padding( - padding: const EdgeInsets.all(8), + padding: const EdgeInsets.only(bottom: 8), child: AspectRatio( aspectRatio: 3 / 2, child: GroupTile(group: activeBoard!), ), ), ), - SliverPadding( - padding: const EdgeInsets.all(8), - sliver: SliverGrid( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - mainAxisSpacing: 8, - crossAxisSpacing: 8, - ), - delegate: SliverChildBuilderDelegate( - (context, index) => GroupTile(group: groups[index]), - childCount: groups.length, - ), + SliverGrid( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + ), + delegate: SliverChildBuilderDelegate( + (context, index) => GroupTile(group: groups[index]), + childCount: groups.length, ), ), ], diff --git a/lib/ui/screens/login_screen.dart b/lib/ui/screens/login_screen.dart index efe7a202b..dfab53680 100644 --- a/lib/ui/screens/login_screen.dart +++ b/lib/ui/screens/login_screen.dart @@ -29,59 +29,63 @@ class _LoginScreenState extends State { builder: (context, authState) { if (authState is LoadingAuthState) { return Scaffold( - backgroundColor: const Color(0xFFE62272), - body: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: Image( - image: logo, - width: 260, - ), - ), - const SizedBox(height: 50), - const SizedBox( - height: 50, - child: Center( - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Colors.white), + backgroundColor: const Color(0xFFE62272), + body: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Image( + image: logo, + width: 260, + ), ), - ), + const SizedBox(height: 50), + const SizedBox( + height: 50, + child: Center( + child: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation(Colors.white), + ), + ), + ), + ], ), - ], - ), - ); + )); } else { return Scaffold( backgroundColor: magenta, - body: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Center( - child: Image( - image: logo, - width: 260, + body: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center( + child: Image( + image: logo, + width: 260, + ), ), - ), - const SizedBox(height: 50), - SizedBox( - height: 50, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.black87, - foregroundColor: Colors.white, + const SizedBox(height: 50), + SizedBox( + height: 50, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.black87, + foregroundColor: Colors.white, + ), + onPressed: () { + BlocProvider.of( + context, + listen: false, + ).logIn(); + }, + child: const Text('LOGIN'), ), - onPressed: () { - BlocProvider.of( - context, - listen: false, - ).logIn(); - }, - child: const Text('LOGIN'), ), - ), - ], + ], + ), ), ); } diff --git a/lib/ui/screens/members_screen.dart b/lib/ui/screens/members_screen.dart index f9ce22f16..418928063 100644 --- a/lib/ui/screens/members_screen.dart +++ b/lib/ui/screens/members_screen.dart @@ -193,41 +193,39 @@ class MemberListScrollView extends StatelessWidget { @override Widget build(BuildContext context) { return Scrollbar( + controller: controller, + child: SafeCustomScrollView( controller: controller, - child: CustomScrollView( - controller: controller, - physics: const RangeMaintainingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), - slivers: [ - SliverPadding( - padding: const EdgeInsets.all(8), - sliver: SliverGrid( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - mainAxisSpacing: 8, - crossAxisSpacing: 8, - ), - delegate: SliverChildBuilderDelegate( - (context, index) => MemberTile( - member: listState.results[index], - ), - childCount: listState.results.length, - ), + physics: const RangeMaintainingScrollPhysics( + parent: AlwaysScrollableScrollPhysics(), + ), + slivers: [ + SliverGrid( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + ), + delegate: SliverChildBuilderDelegate( + (context, index) => MemberTile( + member: listState.results[index], ), + childCount: listState.results.length, ), - if (listState.isLoadingMore) - const SliverPadding( - padding: EdgeInsets.all(8), - sliver: SliverList( - delegate: SliverChildListDelegate.fixed([ - Center( - child: CircularProgressIndicator(), - ) - ]), - ), + ), + if (listState.isLoadingMore) + const SliverPadding( + padding: EdgeInsets.only(top: 8), + sliver: SliverList( + delegate: SliverChildListDelegate.fixed([ + Center( + child: CircularProgressIndicator(), + ) + ]), ), - ], - )); + ), + ], + ), + ); } } diff --git a/lib/ui/screens/profile_screen.dart b/lib/ui/screens/profile_screen.dart index 4913d8289..4f6f848f7 100644 --- a/lib/ui/screens/profile_screen.dart +++ b/lib/ui/screens/profile_screen.dart @@ -462,8 +462,9 @@ class _ProfileScreenState extends State { bloc: _memberCubit, builder: (context, state) { if (state is ErrorState) { - return CustomScrollView( + return SafeCustomScrollView( controller: _scrollController, + top: false, slivers: [ _makeAppBar(), SliverFillRemaining( @@ -472,8 +473,9 @@ class _ProfileScreenState extends State { ], ); } else if (state is LoadingState && widget.member == null) { - return CustomScrollView( + return SafeCustomScrollView( controller: _scrollController, + top: false, slivers: [ _makeAppBar(), const SliverFillRemaining( @@ -482,9 +484,10 @@ class _ProfileScreenState extends State { ], ); } else { - return CustomScrollView( + return SafeCustomScrollView( key: const PageStorageKey('profile'), controller: _scrollController, + top: false, slivers: [ _makeAppBar((state.result ?? widget.member)!), _makeFactsSliver((state.result ?? widget.member)!), @@ -501,7 +504,8 @@ class _ProfileScreenState extends State { ), ), ], - const SliverToBoxAdapter(child: SizedBox(height: 32)) + const SliverToBoxAdapter( + child: SizedBox(height: 32)), // Im pretty sure this can go? ], ); } diff --git a/lib/ui/screens/registration_screen.dart b/lib/ui/screens/registration_screen.dart index c1f49363e..b9044ba0e 100644 --- a/lib/ui/screens/registration_screen.dart +++ b/lib/ui/screens/registration_screen.dart @@ -68,162 +68,164 @@ class _RegistrationScreenState extends State { leading: const CloseButton(), ), body: SingleChildScrollView( - child: Form( - key: _formKey, - child: Column( - children: [ - ...state.result!.entries.map((entry) { - final field = entry.value; - if (field is TextRegistrationField) { - return Column( - children: [ - ListTile( - title: Text(field.label), - subtitle: field.description.isNotEmpty - ? Text(field.description) - : null, - ), - Padding( - padding: const EdgeInsets.only( - left: 16, - bottom: 16, - right: 16, + child: SafeArea( + child: Form( + key: _formKey, + child: Column( + children: [ + ...state.result!.entries.map((entry) { + final field = entry.value; + if (field is TextRegistrationField) { + return Column( + children: [ + ListTile( + title: Text(field.label), + subtitle: field.description.isNotEmpty + ? Text(field.description) + : null, ), - child: TextFormField( - initialValue: field.value, - minLines: 1, - maxLines: 5, - decoration: InputDecoration( - labelText: field.isRequired - ? '${field.label} *' - : field.label, - hintText: 'Lorem ipsum...', + Padding( + padding: const EdgeInsets.only( + left: 16, + bottom: 16, + right: 16, + ), + child: TextFormField( + initialValue: field.value, + minLines: 1, + maxLines: 5, + decoration: InputDecoration( + labelText: field.isRequired + ? '${field.label} *' + : field.label, + hintText: 'Lorem ipsum...', + ), + validator: (value) { + if (field.isRequired && + (value == null || value.isEmpty)) { + return 'Please fill in this field.'; + } + return null; + }, + onSaved: (newValue) => field.value = newValue, ), - validator: (value) { - if (field.isRequired && - (value == null || value.isEmpty)) { - return 'Please fill in this field.'; - } - return null; - }, - onSaved: (newValue) => field.value = newValue, ), - ), - ], - ); - } else if (field is IntegerRegistrationField) { - return Column( - children: [ - ListTile( - dense: field.description.isEmpty, + ], + ); + } else if (field is IntegerRegistrationField) { + return Column( + children: [ + ListTile( + dense: field.description.isEmpty, + title: Text(field.label), + subtitle: field.description.isNotEmpty + ? Text(field.description) + : null, + ), + Padding( + padding: const EdgeInsets.only( + left: 16, + right: 16, + bottom: 16, + ), + child: TextFormField( + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], + decoration: InputDecoration( + labelText: field.isRequired + ? '${field.label} *' + : field.label, + hintText: '123...', + ), + initialValue: field.value?.toString(), + validator: (value) { + if (field.isRequired && + (value == null || value.isEmpty)) { + return 'Please fill in this field.'; + } + return null; + }, + onSaved: (newValue) => + field.value = int.tryParse(newValue!), + ), + ), + ], + ); + } else if (field is CheckboxRegistrationField) { + return Padding( + padding: const EdgeInsets.only(bottom: 16), + child: _CheckboxFormField( + initialValue: field.value ?? false, + onSaved: (newValue) => field.value = newValue, title: Text(field.label), subtitle: field.description.isNotEmpty ? Text(field.description) : null, ), - Padding( - padding: const EdgeInsets.only( - left: 16, - right: 16, - bottom: 16, - ), - child: TextFormField( - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly - ], - decoration: InputDecoration( - labelText: field.isRequired - ? '${field.label} *' - : field.label, - hintText: '123...', - ), - initialValue: field.value?.toString(), - validator: (value) { - if (field.isRequired && - (value == null || value.isEmpty)) { - return 'Please fill in this field.'; - } - return null; - }, - onSaved: (newValue) => - field.value = int.tryParse(newValue!), - ), + ); + } else { + return const SizedBox(height: 0); + } + }), + Padding( + padding: const EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton.icon( + onPressed: () { + _formKey.currentState!.reset(); + }, + icon: const Icon(Icons.restore_page_outlined), + label: const Text('RESTORE'), ), - ], - ); - } else if (field is CheckboxRegistrationField) { - return Padding( - padding: const EdgeInsets.only(bottom: 16), - child: _CheckboxFormField( - initialValue: field.value ?? false, - onSaved: (newValue) => field.value = newValue, - title: Text(field.label), - subtitle: field.description.isNotEmpty - ? Text(field.description) - : null, - ), - ); - } else { - return const SizedBox(height: 0); - } - }), - Padding( - padding: const EdgeInsets.all(16), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton.icon( - onPressed: () { - _formKey.currentState!.reset(); - }, - icon: const Icon(Icons.restore_page_outlined), - label: const Text('RESTORE'), - ), - const SizedBox(width: 16), - ElevatedButton.icon( - onPressed: () async { - if (_formKey.currentState!.validate()) { - _formKey.currentState!.save(); + const SizedBox(width: 16), + ElevatedButton.icon( + onPressed: () async { + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); - final messenger = ScaffoldMessenger.of(context); + final messenger = ScaffoldMessenger.of(context); - try { - await _registrationFieldsCubit.update( - eventPk: widget.eventPk, - registrationPk: widget.registrationPk, - fields: state.result!, - ); + try { + await _registrationFieldsCubit.update( + eventPk: widget.eventPk, + registrationPk: widget.registrationPk, + fields: state.result!, + ); - if (mounted) Navigator.of(context).pop(); + if (mounted) Navigator.of(context).pop(); - messenger.showSnackBar( - const SnackBar( - behavior: SnackBarBehavior.floating, - content: Text( - 'Your registration has been updated.', + messenger.showSnackBar( + const SnackBar( + behavior: SnackBarBehavior.floating, + content: Text( + 'Your registration has been updated.', + ), ), - ), - ); - } on ApiException { - messenger.showSnackBar( - const SnackBar( - behavior: SnackBarBehavior.floating, - content: Text( - 'Could not update your registration.', + ); + } on ApiException { + messenger.showSnackBar( + const SnackBar( + behavior: SnackBarBehavior.floating, + content: Text( + 'Could not update your registration.', + ), ), - ), - ); + ); + } } - } - }, - icon: const Icon(Icons.check), - label: const Text('SUBMIT'), - ), - ], + }, + icon: const Icon(Icons.check), + label: const Text('SUBMIT'), + ), + ], + ), ), - ), - ], + ], + ), ), ), ), diff --git a/lib/ui/screens/welcome_screen.dart b/lib/ui/screens/welcome_screen.dart index 0de6a9e48..1cdbe46ca 100644 --- a/lib/ui/screens/welcome_screen.dart +++ b/lib/ui/screens/welcome_screen.dart @@ -175,22 +175,23 @@ class _WelcomeScreenState extends State { return const Center(child: CircularProgressIndicator()); } else { return Scrollbar( - child: ListView( - key: const PageStorageKey('welcome'), - physics: const AlwaysScrollableScrollPhysics(), - children: [ - _makeSlides(state.slides!), - if (state.slides!.isNotEmpty) const Divider(height: 0), - _makeArticles(state.articles!), - if (state.articles!.isNotEmpty) - const Divider(indent: 16, endIndent: 16, height: 8), - _makeUpcomingEvents(state.upcomingEvents!), - TextButton( - onPressed: () => context.goNamed('calendar'), - child: const Text('SHOW THE ENTIRE AGENDA'), - ), - ], - )); + child: ListView( + key: const PageStorageKey('welcome'), + physics: const AlwaysScrollableScrollPhysics(), + children: [ + _makeSlides(state.slides!), + if (state.slides!.isNotEmpty) const Divider(height: 0), + _makeArticles(state.articles!), + if (state.articles!.isNotEmpty) + const Divider(indent: 16, endIndent: 16, height: 8), + _makeUpcomingEvents(state.upcomingEvents!), + TextButton( + onPressed: () => context.goNamed('calendar'), + child: const Text('SHOW THE ENTIRE AGENDA'), + ), + ], + ), + ); } }, ), diff --git a/lib/ui/widgets.dart b/lib/ui/widgets.dart index e3ffbc54d..3d460c33b 100644 --- a/lib/ui/widgets.dart +++ b/lib/ui/widgets.dart @@ -12,3 +12,4 @@ export 'widgets/push_notification_overlay.dart'; export 'widgets/sales_order_dialog.dart'; export 'widgets/tpay_button.dart'; export 'widgets/group_tile.dart'; +export 'widgets/safe_custom_scrollview.dart'; diff --git a/lib/ui/widgets/safe_custom_scrollview.dart b/lib/ui/widgets/safe_custom_scrollview.dart new file mode 100644 index 000000000..ee82579ce --- /dev/null +++ b/lib/ui/widgets/safe_custom_scrollview.dart @@ -0,0 +1,64 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/widgets.dart'; + +class SafeCustomScrollView extends StatelessWidget { + final List slivers; + final ScrollController? controller; + final ScrollPhysics? physics; + final bool top; + final bool right; + final bool bottom; + final bool left; + final EdgeInsets padding; + + const SafeCustomScrollView({ + required this.slivers, + this.controller, + this.physics, + this.top = true, + this.right = true, + this.bottom = true, + this.left = true, + this.padding = const EdgeInsets.all(8), + super.key, + }); + + @override + Widget build(BuildContext context) { + final MediaQueryData mediaquery = MediaQuery.of(context); + EdgeInsets padding = EdgeInsets.fromLTRB( + max(mediaquery.padding.left, this.padding.left), + max(mediaquery.padding.top, this.padding.top), + max(mediaquery.padding.right, this.padding.right), + max(mediaquery.padding.bottom, this.padding.bottom)); + return SafeArea( + top: false, + bottom: false, + left: left, + right: right, + child: MediaQuery.removePadding( + context: context, + removeLeft: left, + removeTop: top, + removeRight: right, + removeBottom: bottom, + child: Padding( + padding: EdgeInsets.only(left: padding.left, right: padding.right), + child: CustomScrollView( + controller: controller, + physics: physics, + slivers: [ + if (top) + SliverPadding(padding: EdgeInsets.only(top: padding.top)), + ...slivers, + if (bottom) + SliverPadding(padding: EdgeInsets.only(bottom: padding.bottom)), + ], + ), + ), + ), + ); + } +}