Redesign home app bar

This commit is contained in:
Ming Ming 2023-06-10 00:41:24 +08:00
parent e7dd64125e
commit a17a0432c4
3 changed files with 193 additions and 127 deletions

View file

@ -58,6 +58,32 @@ extension ThemeExtension on ThemeData {
}
}
class DarkModeSwitchTheme extends StatelessWidget {
const DarkModeSwitchTheme({
super.key,
required this.child,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Theme(
data: theme.copyWith(
switchTheme: SwitchThemeData(
trackColor: MaterialStateProperty.all(theme.colorScheme.surface),
thumbColor: MaterialStateProperty.all(Colors.black87),
),
colorScheme: theme.colorScheme.copyWith(
outline: Colors.transparent,
),
),
child: child,
);
}
final Widget child;
}
ThemeData buildTheme(Brightness brightness) {
return (brightness == Brightness.light)
? buildLightTheme()
@ -91,19 +117,6 @@ ThemeData buildDarkTheme() {
}
}
ThemeData buildDarkModeSwitchTheme(BuildContext context) {
final theme = Theme.of(context);
return theme.copyWith(
switchTheme: SwitchThemeData(
trackColor: MaterialStateProperty.all(theme.colorScheme.surfaceVariant),
thumbColor: MaterialStateProperty.all(Colors.black87),
),
colorScheme: theme.colorScheme.copyWith(
outline: Colors.transparent,
),
);
}
Color getSeedColor() {
return Color(Pref().getSeedColor() ?? 0xFF2196F3).withAlpha(0xFF);
}

View file

@ -4,6 +4,7 @@ import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:clock/clock.dart';
import 'package:copy_with/copy_with.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kiwi/kiwi.dart';
@ -17,11 +18,13 @@ import 'package:nc_photos/controller/account_controller.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/server_status.dart';
import 'package:nc_photos/entity/sqlite/database.dart' as sql;
import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/exception_event.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
import 'package:nc_photos/help_utils.dart' as help_util;
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/pref.dart';
import 'package:nc_photos/theme.dart';
import 'package:nc_photos/toast.dart';
import 'package:nc_photos/url_launcher_util.dart';
import 'package:nc_photos/widget/home.dart';
@ -88,69 +91,123 @@ class _WrappedAccountPickerDialog extends StatelessWidget {
child: Dialog(
child: Padding(
padding: const EdgeInsets.all(8),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Container(
color: Theme.of(context).colorScheme.background,
child: Material(
type: MaterialType.transparency,
child: _BlocBuilder(
buildWhen: (previous, current) =>
previous.isOpenDropdown != current.isOpenDropdown ||
previous.accounts != current.accounts,
builder: (context, state) {
final bloc = context.read<_Bloc>();
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const _AccountDropdown(),
if (state.isOpenDropdown) ...[
...state.accounts
.where((a) => a.id != bloc.activeAccount.id)
.map((a) => _AccountView(account: a)),
const _NewAccountView(),
] else
const _AccountSettingsView(),
],
);
},
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 8),
Stack(
children: [
Positioned.fill(
child: Center(
child: Text(
L10n.global().appTitle,
style: Theme.of(context).textTheme.headlineSmall,
),
),
),
if (!Pref().isFollowSystemThemeOr(false))
Align(
alignment: AlignmentDirectional.centerEnd,
child: _DarkModeSwitch(
onChanged: _onDarkModeChanged,
),
),
],
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Container(
color: Theme.of(context).colorScheme.background,
child: Material(
type: MaterialType.transparency,
child: _BlocBuilder(
buildWhen: (previous, current) =>
previous.isOpenDropdown !=
current.isOpenDropdown ||
previous.accounts != current.accounts,
builder: (context, state) {
final bloc = context.read<_Bloc>();
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const _AccountDropdown(),
if (state.isOpenDropdown) ...[
...state.accounts
.where(
(a) => a.id != bloc.activeAccount.id)
.map((a) => _AccountView(account: a)),
const _NewAccountView(),
] else
const _AccountSettingsView(),
],
);
},
),
),
),
),
),
_IconTile(
icon: const Icon(Icons.settings_outlined),
title: Text(L10n.global().settingsMenuLabel),
onTap: () {
Navigator.of(context)
..pop()
..pushNamed(
Settings.routeName,
arguments: SettingsArguments(
context.read<_Bloc>().activeAccount),
);
},
),
_IconTile(
icon: const Icon(Icons.help_outline),
title: Text(L10n.global().helpTooltip),
onTap: () {
Navigator.of(context).pop();
launch(help_util.mainUrl);
},
),
const _AboutChin(),
],
_IconTile(
icon: const Icon(Icons.settings_outlined),
title: Text(L10n.global().settingsMenuLabel),
onTap: () {
Navigator.of(context)
..pop()
..pushNamed(
Settings.routeName,
arguments: SettingsArguments(
context.read<_Bloc>().activeAccount),
);
},
),
_IconTile(
icon: const Icon(Icons.help_outline),
title: Text(L10n.global().helpTooltip),
onTap: () {
Navigator.of(context).pop();
launch(help_util.mainUrl);
},
),
const _AboutChin(),
],
),
),
),
),
),
);
}
void _onDarkModeChanged(bool value) {
Pref().setDarkTheme(value).then((_) {
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
});
}
}
class _DarkModeSwitch extends StatelessWidget {
const _DarkModeSwitch({
this.onChanged,
});
@override
Widget build(BuildContext context) {
return DarkModeSwitchTheme(
child: Switch(
value: Theme.of(context).brightness == Brightness.dark,
onChanged: onChanged,
activeThumbImage:
const AssetImage("assets/ic_dark_mode_switch_24dp.png"),
inactiveThumbImage:
const AssetImage("assets/ic_dark_mode_switch_24dp.png"),
),
);
}
final ValueChanged<bool>? onChanged;
}
class _AccountDropdown extends StatelessWidget {

View file

@ -1,10 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:kiwi/kiwi.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/pref.dart';
import 'package:nc_photos/theme.dart';
import 'package:nc_photos/widget/account_picker_dialog.dart';
@ -58,41 +55,39 @@ class HomeSliverAppBar extends StatelessWidget {
],
),
subtitle: accountLabel == null ? Text(account.username2) : null,
icon: isShowProgressIcon
? const AppBarCircularProgressIndicator()
: _LeadingView(account: account),
),
),
scrolledUnderBackgroundColor:
Theme.of(context).homeNavigationBarBackgroundColor,
floating: true,
automaticallyImplyLeading: false,
actions: (actions ?? []) +
[
if (!Pref().isFollowSystemThemeOr(false))
_DarkModeSwitch(
onChanged: _onDarkModeChanged,
),
if (menuActions?.isNotEmpty == true)
PopupMenuButton<int>(
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
itemBuilder: (_) => menuActions!,
onSelected: (option) {
if (option >= 0) {
onSelectedMenuActions?.call(option);
}
},
),
],
actions: [
...actions ?? [],
if (menuActions?.isNotEmpty == true)
PopupMenuButton<int>(
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
itemBuilder: (_) => menuActions!,
onSelected: (option) {
if (option >= 0) {
onSelectedMenuActions?.call(option);
}
},
),
_ProfileIconView(
account: account,
isProcessing: isShowProgressIcon,
onTap: () {
showDialog(
context: context,
builder: (_) => const AccountPickerDialog(),
);
},
),
const SizedBox(width: 8),
],
);
}
void _onDarkModeChanged(bool value) {
Pref().setDarkTheme(value).then((_) {
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
});
}
final Account account;
/// Screen specific action buttons
@ -105,45 +100,46 @@ class HomeSliverAppBar extends StatelessWidget {
final bool isShowProgressIcon;
}
class _DarkModeSwitch extends StatelessWidget {
const _DarkModeSwitch({
this.onChanged,
});
@override
Widget build(BuildContext context) {
return Theme(
data: buildDarkModeSwitchTheme(context),
child: Switch(
value: Theme.of(context).brightness == Brightness.dark,
onChanged: onChanged,
activeThumbImage:
const AssetImage("assets/ic_dark_mode_switch_24dp.png"),
inactiveThumbImage:
const AssetImage("assets/ic_dark_mode_switch_24dp.png"),
),
);
}
final ValueChanged<bool>? onChanged;
}
class _LeadingView extends StatelessWidget {
const _LeadingView({
class _ProfileIconView extends StatelessWidget {
const _ProfileIconView({
required this.account,
required this.isProcessing,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(24),
child: CachedNetworkImage(
imageUrl: api_util.getAccountAvatarUrl(account, 64),
fadeInDuration: const Duration(),
filterQuality: FilterQuality.high,
return SizedBox.square(
dimension: _size,
child: Stack(
children: [
isProcessing
? const AppBarCircularProgressIndicator()
: ClipRRect(
borderRadius: BorderRadius.circular(_size / 2),
child: CachedNetworkImage(
imageUrl: api_util.getAccountAvatarUrl(account, 64),
fadeInDuration: const Duration(),
filterQuality: FilterQuality.high,
),
),
Positioned.fill(
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(_size / 2),
),
),
),
],
),
);
}
final Account account;
final bool isProcessing;
final VoidCallback onTap;
static const _size = 40.0;
}