more refactors
This commit is contained in:
@@ -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: {
|
||||
|
||||
@@ -2,13 +2,28 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:immich_mobile/domain/utils/renderlist_providers.dart';
|
||||
import 'package:immich_mobile/presentation/components/appbar/immich_app_bar.widget.dart';
|
||||
import 'package:immich_mobile/presentation/components/grid/immich_asset_grid.state.dart';
|
||||
import 'package:immich_mobile/presentation/components/grid/immich_asset_grid.widget.dart';
|
||||
import 'package:immich_mobile/utils/extensions/build_context.extension.dart';
|
||||
|
||||
@RoutePage()
|
||||
class HomePage extends StatelessWidget {
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
State createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
final _showAppBar = ValueNotifier<bool>(true);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_showAppBar.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -16,7 +31,35 @@ class HomePage extends StatelessWidget {
|
||||
create: (_) => AssetGridCubit(
|
||||
renderListProvider: RenderListProvider.mainTimeline(),
|
||||
),
|
||||
child: const ImAssetGrid(),
|
||||
child: Stack(children: [
|
||||
ImAssetGrid(
|
||||
topPadding: kToolbarHeight + context.mediaQueryPadding.top - 8,
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _showAppBar,
|
||||
builder: (_, shouldShow, appBar) {
|
||||
final Duration duration;
|
||||
if (shouldShow) {
|
||||
// Animate out app bar slower
|
||||
duration = Durations.short3;
|
||||
} else {
|
||||
// Animate in app bar faster
|
||||
duration = Durations.medium2;
|
||||
}
|
||||
return AnimatedPositioned(
|
||||
duration: duration,
|
||||
curve: Curves.easeOut,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: shouldShow
|
||||
? 0
|
||||
: -(kToolbarHeight + context.mediaQueryPadding.top),
|
||||
child: appBar!,
|
||||
);
|
||||
},
|
||||
child: const ImAppBar(),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/api/user_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/album_sync.service.dart';
|
||||
import 'package:immich_mobile/domain/services/asset_sync.service.dart';
|
||||
import 'package:immich_mobile/domain/services/login.service.dart';
|
||||
import 'package:immich_mobile/i18n/strings.g.dart';
|
||||
import 'package:immich_mobile/presentation/modules/login/models/login_page.model.dart';
|
||||
import 'package:immich_mobile/presentation/states/gallery_permission.state.dart';
|
||||
import 'package:immich_mobile/presentation/states/server_info/server_feature_config.state.dart';
|
||||
import 'package:immich_mobile/service_locator.dart';
|
||||
import 'package:immich_mobile/utils/immich_api_client.dart';
|
||||
import 'package:immich_mobile/utils/mixins/log.mixin.dart';
|
||||
@@ -68,11 +62,7 @@ class LoginPageCubit extends Cubit<LoginPageState> with LogMixin {
|
||||
url = await loginService.resolveEndpoint(uri);
|
||||
|
||||
di<IStoreRepository>().upsert(StoreKey.serverEndpoint, url);
|
||||
await ServiceLocator.registerApiClient(url);
|
||||
ServiceLocator.registerPostGlobalStates();
|
||||
|
||||
// Fetch server features
|
||||
await di<ServerFeatureConfigProvider>().getFeatures();
|
||||
await di<LoginService>().handlePostUrlResolution(url);
|
||||
|
||||
emit(state.copyWith(isServerValidated: true));
|
||||
} finally {
|
||||
@@ -129,20 +119,13 @@ class LoginPageCubit extends Cubit<LoginPageState> with LogMixin {
|
||||
/// Set token to interceptor
|
||||
await di<ImApiClient>().init(accessToken: accessToken);
|
||||
|
||||
final user = await di<IUserApiRepository>().getMyUser();
|
||||
final user = await di<LoginService>().handlePostLogin();
|
||||
if (user == null) {
|
||||
SnackbarManager.showError(t.login.error.error_login);
|
||||
return;
|
||||
}
|
||||
|
||||
// Register user
|
||||
ServiceLocator.registerCurrentUser(user);
|
||||
await di<IUserRepository>().upsert(user);
|
||||
// Remove and Sync assets in background
|
||||
await di<IAssetRepository>().deleteAll();
|
||||
await di<GalleryPermissionProvider>().requestPermission();
|
||||
unawaited(di<AssetSyncService>().performFullRemoteSyncIsolate(user));
|
||||
unawaited(di<AlbumSyncService>().performFullDeviceSyncIsolate());
|
||||
|
||||
emit(state.copyWith(
|
||||
isValidationInProgress: false,
|
||||
|
||||
@@ -11,7 +11,7 @@ import 'package:immich_mobile/presentation/components/input/text_button.widget.d
|
||||
import 'package:immich_mobile/presentation/components/input/text_form_field.widget.dart';
|
||||
import 'package:immich_mobile/presentation/modules/login/models/login_page.model.dart';
|
||||
import 'package:immich_mobile/presentation/modules/login/states/login_page.state.dart';
|
||||
import 'package:immich_mobile/presentation/states/server_info/server_feature_config.state.dart';
|
||||
import 'package:immich_mobile/presentation/states/server_feature_config.state.dart';
|
||||
import 'package:immich_mobile/service_locator.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/i18n/strings.g.dart';
|
||||
import 'package:immich_mobile/presentation/components/image/immich_logo.widget.dart';
|
||||
import 'package:immich_mobile/presentation/components/scaffold/adaptive_route_appbar.widget.dart';
|
||||
import 'package:immich_mobile/utils/constants/globals.dart';
|
||||
import 'package:immich_mobile/utils/constants/size_constants.dart';
|
||||
|
||||
@RoutePage()
|
||||
@@ -19,7 +18,7 @@ class AboutSettingsPage extends StatelessWidget {
|
||||
subtitle: Text(context.t.settings.about.third_party_sub_title),
|
||||
onTap: () => showLicensePage(
|
||||
context: context,
|
||||
applicationName: kImmichAppName,
|
||||
applicationName: context.t.immich,
|
||||
applicationIcon: const ImLogo(width: SizeConstants.xl),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:immich_mobile/presentation/components/scaffold/adaptive_route_ap
|
||||
import 'package:immich_mobile/presentation/components/scaffold/adaptive_route_wrapper.widget.dart';
|
||||
import 'package:immich_mobile/presentation/modules/settings/models/settings_section.model.dart';
|
||||
import 'package:immich_mobile/presentation/router/router.dart';
|
||||
import 'package:immich_mobile/utils/constants/size_constants.dart';
|
||||
import 'package:immich_mobile/utils/extensions/build_context.extension.dart';
|
||||
|
||||
@RoutePage()
|
||||
@@ -16,7 +17,7 @@ class SettingsWrapperPage extends StatelessWidget {
|
||||
return ImAdaptiveRouteWrapper(
|
||||
primaryBody: (_) => const SettingsPage(),
|
||||
primaryRoute: SettingsRoute.name,
|
||||
bodyRatio: 0.3,
|
||||
bodyRatio: BodyRatioConstants.oneThird,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,10 @@ import 'dart:async';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:immich_mobile/domain/services/album_sync.service.dart';
|
||||
import 'package:immich_mobile/domain/services/asset_sync.service.dart';
|
||||
import 'package:immich_mobile/domain/services/login.service.dart';
|
||||
import 'package:immich_mobile/presentation/components/image/immich_logo.widget.dart';
|
||||
import 'package:immich_mobile/presentation/modules/login/states/login_page.state.dart';
|
||||
import 'package:immich_mobile/presentation/router/router.dart';
|
||||
import 'package:immich_mobile/presentation/states/current_user.state.dart';
|
||||
import 'package:immich_mobile/presentation/states/gallery_permission.state.dart';
|
||||
import 'package:immich_mobile/service_locator.dart';
|
||||
import 'package:immich_mobile/utils/mixins/log.mixin.dart';
|
||||
|
||||
@@ -53,11 +49,7 @@ class _SplashScreenState extends State<SplashScreenPage>
|
||||
}
|
||||
|
||||
Future<void> _tryLogin() async {
|
||||
await di<GalleryPermissionProvider>().requestPermission();
|
||||
if (await di<LoginService>().tryAutoLogin() && mounted) {
|
||||
unawaited(di<AssetSyncService>()
|
||||
.performFullRemoteSyncIsolate(di<CurrentUserProvider>().value));
|
||||
unawaited(di<AlbumSyncService>().performFullDeviceSyncIsolate());
|
||||
unawaited(context.replaceRoute(const TabControllerRoute()));
|
||||
} else if (mounted) {
|
||||
unawaited(context.replaceRoute(const LoginRoute()));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/presentation/theme/app_colors.dart';
|
||||
import 'package:immich_mobile/presentation/theme/app_typography.dart';
|
||||
import 'package:immich_mobile/utils/extensions/material_state.extension.dart';
|
||||
|
||||
enum AppTheme {
|
||||
@@ -70,6 +71,23 @@ enum AppTheme {
|
||||
Color.alphaBlend(color.primary.withAlpha(80), color.onSurface)
|
||||
.withAlpha(240),
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
titleLarge: AppTypography.titleLarge,
|
||||
titleMedium: AppTypography.titleMedium,
|
||||
titleSmall: AppTypography.titleSmall,
|
||||
displayLarge: AppTypography.displayLarge,
|
||||
displayMedium: AppTypography.displayMedium,
|
||||
displaySmall: AppTypography.displaySmall,
|
||||
headlineLarge: AppTypography.headlineLarge,
|
||||
headlineMedium: AppTypography.headlineMedium,
|
||||
headlineSmall: AppTypography.headlineSmall,
|
||||
bodyLarge: AppTypography.bodyLarge,
|
||||
bodyMedium: AppTypography.bodyMedium,
|
||||
bodySmall: AppTypography.bodySmall,
|
||||
labelLarge: AppTypography.labelLarge,
|
||||
labelMedium: AppTypography.labelMedium,
|
||||
labelSmall: AppTypography.labelSmall,
|
||||
),
|
||||
snackBarTheme: SnackBarThemeData(
|
||||
elevation: 4,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppTypography {
|
||||
const AppTypography();
|
||||
|
||||
static const TextStyle displayLarge = TextStyle(
|
||||
fontSize: 57,
|
||||
fontWeight: FontWeight.normal,
|
||||
);
|
||||
static const TextStyle displayMedium = TextStyle(
|
||||
fontSize: 45,
|
||||
fontWeight: FontWeight.normal,
|
||||
);
|
||||
static const TextStyle displaySmall = TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.normal,
|
||||
);
|
||||
|
||||
static const TextStyle headlineLarge = TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.normal,
|
||||
);
|
||||
static const TextStyle headlineMedium = TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.normal,
|
||||
);
|
||||
static const TextStyle headlineSmall = TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.normal,
|
||||
);
|
||||
|
||||
static const TextStyle titleLarge = TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
static const TextStyle titleMedium = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
static const TextStyle titleSmall = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
|
||||
static const TextStyle bodyLarge = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal,
|
||||
);
|
||||
static const TextStyle bodyMedium = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
);
|
||||
static const TextStyle bodySmall = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.normal,
|
||||
);
|
||||
|
||||
static const TextStyle labelLarge = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
static const TextStyle labelMedium = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
static const TextStyle labelSmall = TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user