diff --git a/lib/providers/films_provider.dart b/lib/providers/films_provider.dart index 83e6872..b4d6e8b 100644 --- a/lib/providers/films_provider.dart +++ b/lib/providers/films_provider.dart @@ -95,7 +95,6 @@ class FilmsProviderState extends State { setState(() {}); } - // TODO: add delete button to UI void deleteCustomFilm(FilmExponential film) { customFilms.remove(film.id); _discardSelectedIfNotIncluded(); @@ -170,12 +169,7 @@ class Films extends InheritedModel<_FilmsModelAspect> { @override bool updateShouldNotifyDependent(Films oldWidget, Set<_FilmsModelAspect> dependencies) { - if (dependencies.contains(_FilmsModelAspect.customFilmsList)) {} - if (dependencies.contains(_FilmsModelAspect.selected)) { - return selected != oldWidget.selected; - } else { - // TODO: reduce unnecessary notifications - return true; - } + // TODO: reduce unnecessary notifications + return true; } } diff --git a/lib/screens/films/screen_films.dart b/lib/screens/films/screen_films.dart index 2072eed..5a11c2f 100644 --- a/lib/screens/films/screen_films.dart +++ b/lib/screens/films/screen_films.dart @@ -4,6 +4,7 @@ import 'package:lightmeter/navigation/routes.dart'; import 'package:lightmeter/providers/films_provider.dart'; import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/film_edit/flow_film_edit.dart'; +import 'package:lightmeter/screens/shared/sliver_placeholder/widget_sliver_placeholder.dart'; import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @@ -17,6 +18,14 @@ class FilmsScreen extends StatefulWidget { class _FilmsScreenState extends State with SingleTickerProviderStateMixin { late final tabController = TabController(length: 2, vsync: this); + @override + void initState() { + super.initState(); + tabController.addListener(() { + setState(() {}); + }); + } + @override void dispose() { tabController.dispose(); @@ -50,22 +59,19 @@ class _FilmsScreenState extends State with SingleTickerProviderStat ), ], slivers: [ - SliverFillRemaining( - child: TabBarView( - controller: tabController, - children: [ - _FilmsListBuilder( - films: Films.predefinedFilmsOf(context).toList(), - onFilmSelected: FilmsProvider.of(context).toggleFilm, - ), - _FilmsListBuilder( - films: Films.customFilmsOf(context).toList(), - onFilmSelected: FilmsProvider.of(context).toggleFilm, - onFilmEdit: _editFilm, - ), - ], - ), - ), + if (tabController.index == 0) + _FilmsListBuilder( + films: Films.predefinedFilmsOf(context).toList(), + onFilmSelected: FilmsProvider.of(context).toggleFilm, + ) + else if (tabController.index == 1 && Films.customFilmsOf(context).isNotEmpty) + _FilmsListBuilder( + films: Films.customFilmsOf(context).toList(), + onFilmSelected: FilmsProvider.of(context).toggleFilm, + onFilmEdit: _editFilm, + ) + else + SliverPlaceholder(onTap: _addFilm), ], ); } @@ -98,36 +104,41 @@ class _FilmsListBuilder extends StatelessWidget { @override Widget build(BuildContext context) { - return ListView.builder( - padding: const EdgeInsets.all(Dimens.paddingM).add(EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom)), + return SliverList.builder( itemCount: films.length, - itemBuilder: (_, index) => Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: index == 0 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero, - topRight: index == 0 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero, - bottomLeft: index == films.length - 1 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero, - bottomRight: index == films.length - 1 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero, - ), + itemBuilder: (_, index) => Padding( + padding: EdgeInsets.fromLTRB( + Dimens.paddingM, + index == 0 ? Dimens.paddingM : 0, + Dimens.paddingM, + index == films.length - 1 ? Dimens.paddingM + MediaQuery.paddingOf(context).bottom : 0.0, ), - child: Padding( - padding: EdgeInsets.only( - top: index == 0 ? Dimens.paddingM : 0.0, - bottom: index == films.length - 1 ? Dimens.paddingM : 0.0, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: index == 0 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero, + bottom: index == films.length - 1 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero, + ), ), - child: CheckboxListTile( - controlAffinity: ListTileControlAffinity.leading, - value: Films.inUseOf(context).contains(films[index]), - title: Text(films[index].name), - onChanged: (value) { - onFilmSelected(films[index], value ?? false); - }, - secondary: onFilmEdit != null - ? IconButton( - onPressed: () => onFilmEdit!(films[index]), - icon: const Icon(Icons.edit), - ) - : null, + child: Padding( + padding: EdgeInsets.only( + top: index == 0 ? Dimens.paddingM : 0.0, + bottom: index == films.length - 1 ? Dimens.paddingM : 0.0, + ), + child: CheckboxListTile( + controlAffinity: ListTileControlAffinity.leading, + value: Films.inUseOf(context).contains(films[index]), + title: Text(films[index].name), + onChanged: (value) { + onFilmSelected(films[index], value ?? false); + }, + secondary: onFilmEdit != null + ? IconButton( + onPressed: () => onFilmEdit!(films[index]), + icon: const Icon(Icons.edit), + ) + : null, + ), ), ), ), diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart b/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart index 2496977..f13d4ff 100644 --- a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart +++ b/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart @@ -4,7 +4,7 @@ import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart'; import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart'; -import 'package:lightmeter/screens/shared/icon_placeholder/widget_icon_placeholder.dart'; +import 'package:lightmeter/screens/shared/sliver_placeholder/widget_sliver_placeholder.dart'; import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @@ -37,12 +37,7 @@ class _EquipmentProfilesScreenState extends State { ), ], slivers: profilesCount == 1 - ? [ - SliverFillRemaining( - hasScrollBody: false, - child: _EquipmentProfilesListPlaceholder(onTap: _addProfile), - ), - ] + ? [SliverPlaceholder(onTap: _addProfile)] : [ SliverList( delegate: SliverChildBuilderDelegate( @@ -131,32 +126,3 @@ class _EquipmentProfilesScreenState extends State { } } } - -class _EquipmentProfilesListPlaceholder extends StatelessWidget { - final VoidCallback onTap; - - const _EquipmentProfilesListPlaceholder({required this.onTap}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: Dimens.sliverAppBarExpandedHeight), - child: FractionallySizedBox( - widthFactor: 1 / 1.618, - child: Center( - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: onTap, - child: Padding( - padding: const EdgeInsets.all(Dimens.paddingL), - child: IconPlaceholder( - icon: Icons.add_outlined, - text: S.of(context).tapToAdd, - ), - ), - ), - ), - ), - ); - } -} diff --git a/lib/screens/shared/sliver_placeholder/widget_sliver_placeholder.dart b/lib/screens/shared/sliver_placeholder/widget_sliver_placeholder.dart new file mode 100644 index 0000000..a966b51 --- /dev/null +++ b/lib/screens/shared/sliver_placeholder/widget_sliver_placeholder.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/res/dimens.dart'; +import 'package:lightmeter/screens/shared/icon_placeholder/widget_icon_placeholder.dart'; +import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart'; + +class SliverPlaceholder extends StatelessWidget { + final VoidCallback onTap; + + const SliverPlaceholder({required this.onTap}); + + @override + Widget build(BuildContext context) { + final sliverScreenBottomHeight = + context.findAncestorWidgetOfExactType()?.bottom?.preferredSize.height ?? 0.0; + return SliverFillRemaining( + hasScrollBody: false, + child: Padding( + padding: EdgeInsets.only(bottom: Dimens.sliverAppBarExpandedHeight - sliverScreenBottomHeight), + child: FractionallySizedBox( + widthFactor: 1 / 1.618, + child: Center( + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: onTap, + child: Padding( + padding: const EdgeInsets.all(Dimens.paddingL), + child: IconPlaceholder( + icon: Icons.add_outlined, + text: S.of(context).tapToAdd, + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/screens/shared/sliver_screen/screen_sliver.dart b/lib/screens/shared/sliver_screen/screen_sliver.dart index a0de0e4..a35158d 100644 --- a/lib/screens/shared/sliver_screen/screen_sliver.dart +++ b/lib/screens/shared/sliver_screen/screen_sliver.dart @@ -25,36 +25,10 @@ class SliverScreen extends StatelessWidget { bottom: false, child: CustomScrollView( slivers: [ - SliverAppBar.large( - automaticallyImplyLeading: false, - expandedHeight: Dimens.sliverAppBarExpandedHeight + (bottom?.preferredSize.height ?? 0.0), - flexibleSpace: FlexibleSpaceBar( - centerTitle: false, - titlePadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), - title: DefaultTextStyle( - style: Theme.of(context) - .textTheme - .headlineSmall! - .copyWith(color: Theme.of(context).colorScheme.onSurface), - maxLines: 2, - overflow: TextOverflow.ellipsis, - child: _Title( - actionsCount: appBarActions.length + (Navigator.of(context).canPop() ? 1 : 0), - bottomSize: bottom?.preferredSize.height ?? 0.0, - child: title, - ), - ), - ), + _AppBar( + title: title, + appBarActions: appBarActions, bottom: bottom, - actions: [ - ...appBarActions, - if (Navigator.of(context).canPop()) - IconButton( - onPressed: Navigator.of(context).pop, - icon: const Icon(Icons.close_outlined), - tooltip: S.of(context).tooltipClose, - ), - ], ), ...slivers, ], @@ -64,6 +38,51 @@ class SliverScreen extends StatelessWidget { } } +class _AppBar extends StatelessWidget { + final Widget title; + final List appBarActions; + final PreferredSizeWidget? bottom; + + const _AppBar({ + required this.title, + this.appBarActions = const [], + this.bottom, + super.key, + }); + + @override + Widget build(BuildContext context) { + return SliverAppBar.large( + automaticallyImplyLeading: false, + expandedHeight: Dimens.sliverAppBarExpandedHeight + (bottom?.preferredSize.height ?? 0.0), + flexibleSpace: FlexibleSpaceBar( + centerTitle: false, + titlePadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), + title: DefaultTextStyle( + style: Theme.of(context).textTheme.headlineSmall!.copyWith(color: Theme.of(context).colorScheme.onSurface), + maxLines: 2, + overflow: TextOverflow.ellipsis, + child: _Title( + actionsCount: appBarActions.length + (Navigator.of(context).canPop() ? 1 : 0), + bottomSize: bottom?.preferredSize.height ?? 0.0, + child: title, + ), + ), + ), + bottom: bottom, + actions: [ + ...appBarActions, + if (Navigator.of(context).canPop()) + IconButton( + onPressed: Navigator.of(context).pop, + icon: const Icon(Icons.close_outlined), + tooltip: S.of(context).tooltipClose, + ), + ], + ); + } +} + class _Title extends StatelessWidget { final Widget child; final int actionsCount;