more refactors

This commit is contained in:
shenlong-tanwen
2024-10-20 16:50:34 +05:30
parent 7ea21d636f
commit 8f47645cdb
35 changed files with 399 additions and 83 deletions
@@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/i18n/strings.g.dart';
import 'package:immich_mobile/utils/extensions/build_context.extension.dart';
class ImAppBar extends StatelessWidget implements PreferredSizeWidget {
const ImAppBar({super.key});
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
@override
Widget build(BuildContext context) {
return AppBar(
backgroundColor: context.theme.appBarTheme.backgroundColor,
automaticallyImplyLeading: false,
centerTitle: false,
title: Text(context.t.immich),
);
}
}
@@ -75,8 +75,8 @@ class DraggableScrollbar extends StatefulWidget {
this.backgroundColor = Colors.white,
this.foregroundColor = Colors.black,
this.padding,
this.scrollbarAnimationDuration = const Duration(milliseconds: 300),
this.scrollbarTimeToFade = const Duration(milliseconds: 600),
this.scrollbarAnimationDuration = Durations.medium2,
this.scrollbarTimeToFade = Durations.long4,
this.labelTextBuilder,
this.labelConstraints,
}) : assert(child.scrollDirection == Axis.vertical),
@@ -219,6 +219,10 @@ class _DraggableScrollbarState extends State<DraggableScrollbar>
Timer? _fadeoutTimer;
List<FlutterListViewItemPosition> _positions = [];
/// The controller can have only one active callback
/// cache the old one, invoke it in the new callback and restore it on dispose
FlutterSliverListControllerOnPaintItemPositionCallback? _oldCallback;
@override
void initState() {
super.initState();
@@ -246,14 +250,19 @@ class _DraggableScrollbarState extends State<DraggableScrollbar>
curve: Curves.fastOutSlowIn,
);
_oldCallback =
widget.controller.sliverController.onPaintItemPositionsCallback;
widget.controller.sliverController.onPaintItemPositionsCallback =
(height, pos) {
_positions = pos;
_oldCallback?.call(height, pos);
};
}
@override
void dispose() {
widget.controller.sliverController.onPaintItemPositionsCallback =
_oldCallback;
_thumbAnimationController.dispose();
_labelAnimationController.dispose();
_fadeoutTimer?.cancel();
@@ -304,7 +313,9 @@ class _DraggableScrollbarState extends State<DraggableScrollbar>
}
double get _barMaxScrollExtent =>
(context.size?.height ?? 0) - widget.heightScrollThumb;
(context.size?.height ?? 0) -
widget.heightScrollThumb -
(widget.padding?.vertical ?? 0);
double get _maxScrollRatio =>
_barMaxScrollExtent / widget.controller.position.maxScrollExtent;
@@ -414,7 +425,7 @@ class _DraggableScrollbarState extends State<DraggableScrollbar>
widget.scrollStateListener(true);
_dragHaltTimer = Timer(
const Duration(milliseconds: 500),
Durations.long2,
() => widget.scrollStateListener(false),
);
}
@@ -8,7 +8,27 @@ import 'package:immich_mobile/domain/models/render_list.model.dart';
import 'package:immich_mobile/domain/utils/renderlist_providers.dart';
import 'package:immich_mobile/utils/constants/globals.dart';
class AssetGridCubit extends Cubit<RenderList> {
class AssetGridState {
final bool isDragScrolling;
final RenderList renderList;
const AssetGridState({
required this.isDragScrolling,
required this.renderList,
});
factory AssetGridState.empty() =>
AssetGridState(isDragScrolling: false, renderList: RenderList.empty());
AssetGridState copyWith({bool? isDragScrolling, RenderList? renderList}) {
return AssetGridState(
isDragScrolling: isDragScrolling ?? this.isDragScrolling,
renderList: renderList ?? this.renderList,
);
}
}
class AssetGridCubit extends Cubit<AssetGridState> {
final RenderListProvider _renderListProvider;
late final StreamSubscription _renderListSubscription;
@@ -20,21 +40,27 @@ class AssetGridCubit extends Cubit<RenderList> {
AssetGridCubit({required RenderListProvider renderListProvider})
: _renderListProvider = renderListProvider,
super(RenderList.empty()) {
super(AssetGridState.empty()) {
_renderListSubscription =
_renderListProvider.renderStreamProvider().listen((renderList) {
_bufOffset = 0;
_buf = [];
emit(renderList);
emit(state.copyWith(renderList: renderList));
});
}
void setDragScrolling(bool isScrolling) {
if (state.isDragScrolling != isScrolling) {
emit(state.copyWith(isDragScrolling: isScrolling));
}
}
/// Loads the requested assets from the database to an internal buffer if not cached
/// and returns a slice of that buffer
Future<List<Asset>> loadAssets(int offset, int count) async {
assert(offset >= 0);
assert(count > 0);
assert(offset + count <= state.totalCount);
assert(offset + count <= state.renderList.totalCount);
// the requested slice (offset:offset+count) is not contained in the cache buffer `_buf`
// thus, fill the buffer with a new batch of assets that at least contains the requested
@@ -1,7 +1,7 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_list_view/flutter_list_view.dart';
import 'package:immich_mobile/domain/models/render_list.model.dart';
import 'package:immich_mobile/domain/models/render_list_element.model.dart';
import 'package:immich_mobile/presentation/components/grid/draggable_scrollbar.dart';
import 'package:immich_mobile/presentation/components/grid/immich_asset_grid.state.dart';
@@ -15,28 +15,33 @@ import 'package:material_symbols_icons/symbols.dart';
part 'immich_asset_grid_header.widget.dart';
class ImAssetGrid extends StatefulWidget {
const ImAssetGrid({super.key});
/// The padding for the grid
final double? topPadding;
final FlutterListViewController? controller;
const ImAssetGrid({this.controller, this.topPadding, super.key});
@override
State createState() => _ImAssetGridState();
}
class _ImAssetGridState extends State<ImAssetGrid> {
bool _isDragScrolling = false;
final FlutterListViewController _controller = FlutterListViewController();
late final FlutterListViewController _controller;
@override
void initState() {
super.initState();
_controller = widget.controller ?? FlutterListViewController();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _onDragScrolling(bool isScrolling) {
if (_isDragScrolling != isScrolling) {
setState(() {
_isDragScrolling = isScrolling;
});
// Dispose controller if it was created here
if (widget.controller == null) {
_controller.dispose();
}
super.dispose();
}
Text? _labelBuilder(List<RenderListElement> elements, int currentPosition) {
@@ -55,9 +60,21 @@ class _ImAssetGridState extends State<ImAssetGrid> {
}
@override
Widget build(BuildContext context) => BlocBuilder<AssetGridCubit, RenderList>(
builder: (_, renderList) {
final elements = renderList.elements;
Widget build(BuildContext context) =>
BlocBuilder<AssetGridCubit, AssetGridState>(
builder: (_, state) {
final elements = state.renderList.elements;
if (widget.topPadding != null &&
elements.firstOrNull is! RenderListPaddingElement) {
elements.insert(
0,
RenderListPaddingElement.beforeElement(
top: widget.topPadding!,
before: elements.firstOrNull,
),
);
}
final grid = FlutterListView(
controller: _controller,
delegate: FlutterListViewDelegate(
@@ -66,6 +83,9 @@ class _ImAssetGridState extends State<ImAssetGrid> {
final section = elements[sectionIndex];
return switch (section) {
RenderListPaddingElement() => Padding(
padding: EdgeInsets.only(top: section.topPadding),
),
RenderListMonthHeaderElement() =>
_MonthHeader(text: section.header),
RenderListDayHeaderElement() => Text(section.header),
@@ -95,7 +115,7 @@ class _ImAssetGridState extends State<ImAssetGrid> {
return SizedBox.square(
dimension: 200,
// Show Placeholder when drag scrolled
child: asset == null || _isDragScrolling
child: asset == null || state.isDragScrolling
? const ImImagePlaceholder()
: ImThumbnail(asset),
);
@@ -111,17 +131,26 @@ class _ImAssetGridState extends State<ImAssetGrid> {
),
);
final EdgeInsetsGeometry? padding;
if (widget.topPadding != null) {
padding = EdgeInsets.only(top: widget.topPadding!);
} else {
padding = null;
}
return DraggableScrollbar(
foregroundColor: context.colorScheme.onSurface,
backgroundColor: context.colorScheme.surfaceContainerHighest,
scrollStateListener: _onDragScrolling,
scrollStateListener:
context.read<AssetGridCubit>().setDragScrolling,
controller: _controller,
maxItemCount: elements.length,
labelTextBuilder: (int position) =>
_labelBuilder(elements, position),
labelConstraints: const BoxConstraints(maxHeight: 36),
scrollbarAnimationDuration: const Duration(milliseconds: 300),
scrollbarTimeToFade: const Duration(milliseconds: 1000),
scrollbarAnimationDuration: Durations.medium2,
scrollbarTimeToFade: Durations.extralong4,
padding: padding,
child: grid,
);
},
@@ -64,7 +64,7 @@ class ImImage extends StatelessWidget {
Widget build(BuildContext context) {
return OctoImage(
fadeInDuration: const Duration(milliseconds: 0),
fadeOutDuration: const Duration(milliseconds: 200),
fadeOutDuration: Durations.short4,
placeholderBuilder: (_) => placeholder,
image: ImImage.imageProvider(asset: asset),
width: width,
@@ -22,7 +22,7 @@ class ImAdaptiveScaffoldBody extends StatelessWidget {
Widget build(BuildContext context) {
return AdaptiveLayout(
internalAnimations: false,
transitionDuration: const Duration(milliseconds: 300),
transitionDuration: Durations.medium2,
bodyRatio: bodyRatio,
body: SlotLayout(
config: {