ui backbone

This commit is contained in:
Mehul Ahal 2022-12-20 21:52:24 +01:00
parent 3e374d24f6
commit b7045fc242
24 changed files with 918 additions and 73 deletions

View file

@ -1,10 +1,71 @@
import 'package:flutter/widgets.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import '/features/core/data/constants/const_colors.dart';
import '/features/core/data/constants/const_durations.dart';
import '/features/core/widgets/mcg_scaffold.dart';
import '/features/core/widgets/view_model_builder.dart';
import 'gallery_view_model.dart';
class GalleryView extends StatelessWidget {
const GalleryView({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
return ViewModelBuilder<GalleryViewModel>(
viewModelBuilder: () => GalleryViewModel.locate,
builder: (context, final model) => McgScaffold(
bodyBuilderWaiter: model.isInitialised,
appBar: AppBar(
title: Text(model.strings.gallery),
),
body: Center(
child: ValueListenableBuilder<bool>(
valueListenable: model.isDisplayingPressingPrompt,
builder: (context, final isDisplayingPressingPrompt, _) => AnimatedSwitcher(
duration: ConstDurations.defaultAnimationDuration,
child: isDisplayingPressingPrompt
? ElevatedButton(
onPressed: model.onPromptPressed,
child: Text(model.strings.startLoadingPrompt),
)
: DecoratedBox(
decoration: const BoxDecoration(color: ConstColours.galleryBackgroundColour),
child: SingleChildScrollView(
// Using Wrap instead of GridView, to make use of different image sizes
child: Wrap(
runSpacing: 24,
spacing: 8,
alignment: WrapAlignment.center,
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
for (final imageModel in model.imageModels)
GestureDetector(
onTap: () => model.pushImageCarouselView(
context,
imageModel: imageModel,
),
child: Hero(
tag: imageModel.imageIndex.toString(),
child: CachedNetworkImage(
imageUrl: imageModel.uri.toString(),
cacheKey: imageModel.imageIndex.toString(),
progressIndicatorBuilder: (_, __, final progress) =>
CircularProgressIndicator(
value: model.downloadProgressValue(progress: progress),
),
),
),
),
],
),
),
),
),
),
),
),
);
}
}

View file

@ -0,0 +1,75 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:mc_gallery/features/core/services/app_lifecycle_service.dart';
import '/features/core/abstracts/base_view_model.dart';
import '/features/core/services/logging_service.dart';
import '/features/core/services/navigation_service.dart';
import '/features/home/data/models/image_model.dart';
import '/features/home/services/images_service.dart';
import '/features/home/views/image_carousel/image_carousel_view.dart';
import '/locator.dart';
class GalleryViewModel extends BaseViewModel {
GalleryViewModel({
required ImagesService imagesService,
required NavigationService navigationService,
required AppLifecycleService appLifecycleService,
required LoggingService loggingService,
}) : _imagesService = imagesService,
_navigationService = navigationService,
_appLifecycleService = appLifecycleService,
_loggingService = loggingService;
final ImagesService _imagesService;
final NavigationService _navigationService;
final AppLifecycleService _appLifecycleService;
final LoggingService _loggingService;
final ValueNotifier<bool> _isDisplayingPressingPrompt = ValueNotifier(true);
ValueListenable<bool> get isDisplayingPressingPrompt => _isDisplayingPressingPrompt;
@override
Future<void> initialise(bool Function() mounted, [arguments]) async {
_appLifecycleService.addListener(
tag: runtimeType.toString(),
listener: (final appLifecycleState) async {
switch (appLifecycleState) {
case AppLifecycleState.resumed:
break;
case AppLifecycleState.inactive:
case AppLifecycleState.paused:
case AppLifecycleState.detached:
await DefaultCacheManager().emptyCache();
}
},
);
super.initialise(mounted, arguments);
}
@override
Future<void> dispose() async {
await _appLifecycleService.removeListener(tag: runtimeType.toString());
super.dispose();
}
void onPromptPressed() => _isDisplayingPressingPrompt.value = false;
Iterable<ImageModel> get imageModels => _imagesService.imageModels;
void pushImageCarouselView(BuildContext context, {required ImageModel imageModel}) =>
_navigationService.pushImageCarouselView(
context,
imageCarouselViewArguments: ImageCarouselViewArguments(
imageIndexKey: imageModel.imageIndex,
),
);
static GalleryViewModel get locate => Locator.locate();
double? downloadProgressValue({required DownloadProgress progress}) =>
progress.totalSize != null ? progress.downloaded / progress.totalSize! : null;
}