chore: style grid
This commit is contained in:
@@ -3,8 +3,13 @@ import 'package:immich_mobile/domain/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/render_list_element.model.dart';
|
||||
import 'package:immich_mobile/presentation/components/image/immich_image.widget.dart';
|
||||
import 'package:immich_mobile/service_locator.dart';
|
||||
import 'package:immich_mobile/utils/extensions/async_snapshot.extension.dart';
|
||||
import 'package:immich_mobile/utils/extensions/build_context.extension.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
part 'immich_asset_grid_header.widget.dart';
|
||||
|
||||
class ImAssetGrid extends StatelessWidget {
|
||||
const ImAssetGrid({super.key});
|
||||
|
||||
@@ -21,11 +26,14 @@ class ImAssetGrid extends StatelessWidget {
|
||||
final elements = renderList.elements;
|
||||
return ScrollablePositionedList.builder(
|
||||
itemCount: elements.length,
|
||||
addAutomaticKeepAlives: false,
|
||||
minCacheExtent: 100,
|
||||
itemBuilder: (_, sectionIndex) {
|
||||
final section = elements[sectionIndex];
|
||||
|
||||
return switch (section) {
|
||||
RenderListMonthHeaderElement() => Text(section.header),
|
||||
RenderListMonthHeaderElement() =>
|
||||
_MonthHeader(text: section.header),
|
||||
RenderListDayHeaderElement() => Text(section.header),
|
||||
RenderListAssetElement() => FutureBuilder(
|
||||
future: renderList.loadAssets(
|
||||
@@ -34,12 +42,11 @@ class ImAssetGrid extends StatelessWidget {
|
||||
),
|
||||
builder: (_, assetsSnap) {
|
||||
final assets = assetsSnap.data;
|
||||
if (assets == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return GridView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
addAutomaticKeepAlives: false,
|
||||
cacheExtent: 100,
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4,
|
||||
@@ -47,8 +54,10 @@ class ImAssetGrid extends StatelessWidget {
|
||||
itemBuilder: (_, i) {
|
||||
return SizedBox.square(
|
||||
dimension: 200,
|
||||
// ignore: avoid-unsafe-collection-methods
|
||||
child: ImImage(assets.elementAt(i)),
|
||||
child: assetsSnap.isWaiting || assets == null
|
||||
? Container(color: Colors.grey)
|
||||
// ignore: avoid-unsafe-collection-methods
|
||||
: ImImage(assets[i]),
|
||||
);
|
||||
},
|
||||
itemCount: section.assetCount,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
part of 'immich_asset_grid.widget.dart';
|
||||
|
||||
class _HeaderText extends StatelessWidget {
|
||||
final String text;
|
||||
final TextStyle? style;
|
||||
|
||||
const _HeaderText({required this.text, this.style});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 32.0, left: 16.0, right: 12.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(text, style: style),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
// ignore: no-empty-block
|
||||
onPressed: () {},
|
||||
icon: Icon(
|
||||
Symbols.check_circle_rounded,
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MonthHeader extends StatelessWidget {
|
||||
final String text;
|
||||
|
||||
const _MonthHeader({required this.text});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _HeaderText(
|
||||
text: text,
|
||||
style: context.textTheme.bodyLarge?.copyWith(fontSize: 24.0),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,6 @@ class HomePage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const ImAssetGrid();
|
||||
return const Scaffold(body: ImAssetGrid());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ class _LoginPageState extends State<LoginPage>
|
||||
child: Column(children: [
|
||||
Expanded(child: rotatingLogo),
|
||||
serverUrl,
|
||||
const SizedGap.sh(),
|
||||
Expanded(flex: 2, child: form),
|
||||
bottom,
|
||||
]),
|
||||
|
||||
@@ -66,7 +66,8 @@ class LoginPageCubit extends Cubit<LoginPageState> with LogContext {
|
||||
url = await loginService.resolveEndpoint(uri);
|
||||
|
||||
di<IStoreRepository>().set(StoreKey.serverEndpoint, url);
|
||||
ServiceLocator.registerPostValidationServices(url);
|
||||
ServiceLocator.registerApiClient(url);
|
||||
ServiceLocator.registerPostValidationServices();
|
||||
ServiceLocator.registerPostGlobalStates();
|
||||
|
||||
// Fetch server features
|
||||
|
||||
@@ -32,28 +32,28 @@ class LoginForm extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocSelector<LoginPageCubit, LoginPageState, bool>(
|
||||
selector: (model) => model.isServerValidated,
|
||||
builder: (_, isServerValidated) => AnimatedSwitcher(
|
||||
duration: Durations.medium1,
|
||||
child: SingleChildScrollView(
|
||||
builder: (_, isServerValidated) => SingleChildScrollView(
|
||||
child: AnimatedSwitcher(
|
||||
duration: Durations.medium1,
|
||||
child: isServerValidated
|
||||
? _CredentialsPage(
|
||||
? _CredentialsForm(
|
||||
emailController: emailController,
|
||||
passwordController: passwordController,
|
||||
)
|
||||
: _ServerPage(controller: serverUrlController),
|
||||
: _ServerForm(controller: serverUrlController),
|
||||
layoutBuilder: (current, previous) =>
|
||||
current ?? (previous.lastOrNull ?? const SizedBox.shrink()),
|
||||
),
|
||||
layoutBuilder: (current, previous) =>
|
||||
current ?? (previous.lastOrNull ?? const SizedBox.shrink()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ServerPage extends StatelessWidget {
|
||||
class _ServerForm extends StatelessWidget {
|
||||
final TextEditingController controller;
|
||||
final GlobalKey<FormState> _formKey = GlobalKey();
|
||||
|
||||
_ServerPage({required this.controller});
|
||||
_ServerForm({required this.controller});
|
||||
|
||||
Future<void> _validateForm(BuildContext context) async {
|
||||
if (_formKey.currentState?.validate() == true) {
|
||||
@@ -96,20 +96,20 @@ class _ServerPage extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _CredentialsPage extends StatefulWidget {
|
||||
class _CredentialsForm extends StatefulWidget {
|
||||
final TextEditingController emailController;
|
||||
final TextEditingController passwordController;
|
||||
|
||||
const _CredentialsPage({
|
||||
const _CredentialsForm({
|
||||
required this.emailController,
|
||||
required this.passwordController,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_CredentialsPage> createState() => _CredentialsPageState();
|
||||
State<_CredentialsForm> createState() => _CredentialsFormState();
|
||||
}
|
||||
|
||||
class _CredentialsPageState extends State<_CredentialsPage> {
|
||||
class _CredentialsFormState extends State<_CredentialsForm> {
|
||||
final passwordFocusNode = FocusNode();
|
||||
|
||||
@override
|
||||
|
||||
@@ -23,9 +23,10 @@ abstract class AppColors {
|
||||
onError: Color(0xfffffbff),
|
||||
errorContainer: Color(0xffffdad6),
|
||||
onErrorContainer: Color(0xff410002),
|
||||
surface: Color(0xfffefbff),
|
||||
surface: Color(0xFFF0EFF4),
|
||||
onSurface: Color(0xff1a1b21),
|
||||
onSurfaceVariant: Color(0xff444651),
|
||||
surfaceContainer: Color(0xfffefbff),
|
||||
surfaceContainerHighest: Color(0xffe0e2ef),
|
||||
outline: Color(0xff747782),
|
||||
outlineVariant: Color(0xffc4c6d3),
|
||||
@@ -55,9 +56,10 @@ abstract class AppColors {
|
||||
onError: Color(0xff410002),
|
||||
errorContainer: Color(0xff93000a),
|
||||
onErrorContainer: Color(0xffffb4ab),
|
||||
surface: Color(0xff1a1e22),
|
||||
surface: Color(0xFF15181C),
|
||||
onSurface: Color(0xffe2e2e9),
|
||||
onSurfaceVariant: Color(0xffc2c6d2),
|
||||
surfaceContainer: Color(0xff1a1e22),
|
||||
surfaceContainerHighest: Color(0xff424852),
|
||||
outline: Color(0xff8c919c),
|
||||
outlineVariant: Color(0xff424751),
|
||||
|
||||
@@ -18,7 +18,7 @@ enum AppTheme {
|
||||
primaryColor: color.primary,
|
||||
iconTheme: const IconThemeData(weight: 500, opticalSize: 24),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
backgroundColor: color.surface,
|
||||
backgroundColor: color.surfaceContainer,
|
||||
indicatorColor: color.primary,
|
||||
iconTheme: WidgetStateProperty.resolveWith(
|
||||
(Set<WidgetState> states) {
|
||||
@@ -29,8 +29,9 @@ enum AppTheme {
|
||||
},
|
||||
),
|
||||
),
|
||||
scaffoldBackgroundColor: color.surface,
|
||||
navigationRailTheme: NavigationRailThemeData(
|
||||
backgroundColor: color.surface,
|
||||
backgroundColor: color.surfaceContainer,
|
||||
elevation: 3,
|
||||
indicatorColor: color.primary,
|
||||
selectedIconTheme:
|
||||
@@ -41,9 +42,21 @@ enum AppTheme {
|
||||
color: color.onSurface.withAlpha(175),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
border: OutlineInputBorder(),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: color.primary),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: color.outlineVariant),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
||||
),
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
textSelectionTheme: TextSelectionThemeData(cursorColor: color.primary),
|
||||
sliderTheme: SliderThemeData(
|
||||
valueIndicatorColor:
|
||||
Color.alphaBlend(color.primary.withAlpha(80), color.onSurface)
|
||||
|
||||
Reference in New Issue
Block a user