mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-21 23:10:40 +00:00
added placeholder for empty custom films list
This commit is contained in:
parent
68cecf5391
commit
45d7728c85
5 changed files with 145 additions and 116 deletions
|
@ -95,7 +95,6 @@ class FilmsProviderState extends State<FilmsProvider> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add delete button to UI
|
|
||||||
void deleteCustomFilm(FilmExponential film) {
|
void deleteCustomFilm(FilmExponential film) {
|
||||||
customFilms.remove(film.id);
|
customFilms.remove(film.id);
|
||||||
_discardSelectedIfNotIncluded();
|
_discardSelectedIfNotIncluded();
|
||||||
|
@ -170,12 +169,7 @@ class Films extends InheritedModel<_FilmsModelAspect> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotifyDependent(Films oldWidget, Set<_FilmsModelAspect> dependencies) {
|
bool updateShouldNotifyDependent(Films oldWidget, Set<_FilmsModelAspect> dependencies) {
|
||||||
if (dependencies.contains(_FilmsModelAspect.customFilmsList)) {}
|
// TODO: reduce unnecessary notifications
|
||||||
if (dependencies.contains(_FilmsModelAspect.selected)) {
|
return true;
|
||||||
return selected != oldWidget.selected;
|
|
||||||
} else {
|
|
||||||
// TODO: reduce unnecessary notifications
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:lightmeter/navigation/routes.dart';
|
||||||
import 'package:lightmeter/providers/films_provider.dart';
|
import 'package:lightmeter/providers/films_provider.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/film_edit/flow_film_edit.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:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.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 {
|
class _FilmsScreenState extends State<FilmsScreen> with SingleTickerProviderStateMixin {
|
||||||
late final tabController = TabController(length: 2, vsync: this);
|
late final tabController = TabController(length: 2, vsync: this);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
tabController.addListener(() {
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
tabController.dispose();
|
tabController.dispose();
|
||||||
|
@ -50,22 +59,19 @@ class _FilmsScreenState extends State<FilmsScreen> with SingleTickerProviderStat
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverFillRemaining(
|
if (tabController.index == 0)
|
||||||
child: TabBarView(
|
_FilmsListBuilder(
|
||||||
controller: tabController,
|
films: Films.predefinedFilmsOf(context).toList(),
|
||||||
children: [
|
onFilmSelected: FilmsProvider.of(context).toggleFilm,
|
||||||
_FilmsListBuilder(
|
)
|
||||||
films: Films.predefinedFilmsOf(context).toList(),
|
else if (tabController.index == 1 && Films.customFilmsOf(context).isNotEmpty)
|
||||||
onFilmSelected: FilmsProvider.of(context).toggleFilm,
|
_FilmsListBuilder<FilmExponential>(
|
||||||
),
|
films: Films.customFilmsOf(context).toList(),
|
||||||
_FilmsListBuilder<FilmExponential>(
|
onFilmSelected: FilmsProvider.of(context).toggleFilm,
|
||||||
films: Films.customFilmsOf(context).toList(),
|
onFilmEdit: _editFilm,
|
||||||
onFilmSelected: FilmsProvider.of(context).toggleFilm,
|
)
|
||||||
onFilmEdit: _editFilm,
|
else
|
||||||
),
|
SliverPlaceholder(onTap: _addFilm),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -98,36 +104,41 @@ class _FilmsListBuilder<T extends Film> extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListView.builder(
|
return SliverList.builder(
|
||||||
padding: const EdgeInsets.all(Dimens.paddingM).add(EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom)),
|
|
||||||
itemCount: films.length,
|
itemCount: films.length,
|
||||||
itemBuilder: (_, index) => Card(
|
itemBuilder: (_, index) => Padding(
|
||||||
shape: RoundedRectangleBorder(
|
padding: EdgeInsets.fromLTRB(
|
||||||
borderRadius: BorderRadius.only(
|
Dimens.paddingM,
|
||||||
topLeft: index == 0 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero,
|
index == 0 ? Dimens.paddingM : 0,
|
||||||
topRight: index == 0 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero,
|
Dimens.paddingM,
|
||||||
bottomLeft: index == films.length - 1 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero,
|
index == films.length - 1 ? Dimens.paddingM + MediaQuery.paddingOf(context).bottom : 0.0,
|
||||||
bottomRight: index == films.length - 1 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Card(
|
||||||
padding: EdgeInsets.only(
|
shape: RoundedRectangleBorder(
|
||||||
top: index == 0 ? Dimens.paddingM : 0.0,
|
borderRadius: BorderRadius.vertical(
|
||||||
bottom: index == films.length - 1 ? Dimens.paddingM : 0.0,
|
top: index == 0 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero,
|
||||||
|
bottom: index == films.length - 1 ? const Radius.circular(Dimens.borderRadiusL) : Radius.zero,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: CheckboxListTile(
|
child: Padding(
|
||||||
controlAffinity: ListTileControlAffinity.leading,
|
padding: EdgeInsets.only(
|
||||||
value: Films.inUseOf(context).contains(films[index]),
|
top: index == 0 ? Dimens.paddingM : 0.0,
|
||||||
title: Text(films[index].name),
|
bottom: index == films.length - 1 ? Dimens.paddingM : 0.0,
|
||||||
onChanged: (value) {
|
),
|
||||||
onFilmSelected(films[index], value ?? false);
|
child: CheckboxListTile(
|
||||||
},
|
controlAffinity: ListTileControlAffinity.leading,
|
||||||
secondary: onFilmEdit != null
|
value: Films.inUseOf(context).contains(films[index]),
|
||||||
? IconButton(
|
title: Text(films[index].name),
|
||||||
onPressed: () => onFilmEdit!(films[index]),
|
onChanged: (value) {
|
||||||
icon: const Icon(Icons.edit),
|
onFilmSelected(films[index], value ?? false);
|
||||||
)
|
},
|
||||||
: null,
|
secondary: onFilmEdit != null
|
||||||
|
? IconButton(
|
||||||
|
onPressed: () => onFilmEdit!(films[index]),
|
||||||
|
icon: const Icon(Icons.edit),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||||
import 'package:lightmeter/res/dimens.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_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/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:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
|
@ -37,12 +37,7 @@ class _EquipmentProfilesScreenState extends State<EquipmentProfilesScreen> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
slivers: profilesCount == 1
|
slivers: profilesCount == 1
|
||||||
? [
|
? [SliverPlaceholder(onTap: _addProfile)]
|
||||||
SliverFillRemaining(
|
|
||||||
hasScrollBody: false,
|
|
||||||
child: _EquipmentProfilesListPlaceholder(onTap: _addProfile),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: [
|
: [
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,36 +25,10 @@ class SliverScreen extends StatelessWidget {
|
||||||
bottom: false,
|
bottom: false,
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
SliverAppBar.large(
|
_AppBar(
|
||||||
automaticallyImplyLeading: false,
|
title: title,
|
||||||
expandedHeight: Dimens.sliverAppBarExpandedHeight + (bottom?.preferredSize.height ?? 0.0),
|
appBarActions: appBarActions,
|
||||||
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,
|
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,
|
...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 {
|
class _Title extends StatelessWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final int actionsCount;
|
final int actionsCount;
|
||||||
|
|
Loading…
Reference in a new issue