added placeholder for empty custom films list

This commit is contained in:
Vadim 2024-10-26 12:55:55 +02:00
parent 68cecf5391
commit 45d7728c85
5 changed files with 145 additions and 116 deletions

View file

@ -95,7 +95,6 @@ class FilmsProviderState extends State<FilmsProvider> {
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;
}
}

View file

@ -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<FilmsScreen> 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<FilmsScreen> with SingleTickerProviderStat
),
],
slivers: [
SliverFillRemaining(
child: TabBarView(
controller: tabController,
children: [
_FilmsListBuilder(
films: Films.predefinedFilmsOf(context).toList(),
onFilmSelected: FilmsProvider.of(context).toggleFilm,
),
_FilmsListBuilder<FilmExponential>(
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<FilmExponential>(
films: Films.customFilmsOf(context).toList(),
onFilmSelected: FilmsProvider.of(context).toggleFilm,
onFilmEdit: _editFilm,
)
else
SliverPlaceholder(onTap: _addFilm),
],
);
}
@ -98,36 +104,41 @@ class _FilmsListBuilder<T extends Film> 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,
),
),
),
),

View file

@ -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<EquipmentProfilesScreen> {
),
],
slivers: profilesCount == 1
? [
SliverFillRemaining(
hasScrollBody: false,
child: _EquipmentProfilesListPlaceholder(onTap: _addProfile),
),
]
? [SliverPlaceholder(onTap: _addProfile)]
: [
SliverList(
delegate: SliverChildBuilderDelegate(
@ -131,32 +126,3 @@ class _EquipmentProfilesScreenState extends State<EquipmentProfilesScreen> {
}
}
}
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,
),
),
),
),
),
);
}
}

View file

@ -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<SliverScreen>()?.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,
),
),
),
),
),
),
);
}
}

View file

@ -25,36 +25,10 @@ class SliverScreen extends StatelessWidget {
bottom: false,
child: CustomScrollView(
slivers: <Widget>[
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<Widget> 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;