M3 Overhaul

This commit is contained in:
2026-03-06 20:03:32 +08:00
parent 82fe619f22
commit 73dc735cce
32 changed files with 1940 additions and 682 deletions
+41 -26
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import '../theme/m3_motion.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
// showcaseview removed due to null-safety incompatibility; onboarding shown via dialog
@@ -7,6 +8,7 @@ import '../providers/auth_provider.dart';
import '../providers/notifications_provider.dart';
import '../providers/profile_provider.dart';
import 'app_breakpoints.dart';
import 'profile_avatar.dart';
final GlobalKey notificationBellKey = GlobalKey();
@@ -49,7 +51,7 @@ class AppScaffold extends ConsumerWidget {
appBar: AppBar(
title: Row(
children: [
const Icon(Icons.memory),
Image.asset('assets/tasq_ico.png', width: 28, height: 28),
const SizedBox(width: 8),
Text('TasQ'),
],
@@ -73,7 +75,7 @@ class AppScaffold extends ConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
const Icon(Icons.account_circle),
ProfileAvatar(fullName: displayName, radius: 16),
const SizedBox(width: 8),
Text(displayName),
const SizedBox(width: 4),
@@ -89,7 +91,7 @@ class AppScaffold extends ConsumerWidget {
IconButton(
tooltip: 'Profile',
onPressed: () => context.go('/profile'),
icon: const Icon(Icons.account_circle),
icon: ProfileAvatar(fullName: displayName, radius: 16),
),
IconButton(
tooltip: 'Sign out',
@@ -164,23 +166,44 @@ class AppNavigationRail extends StatelessWidget {
@override
Widget build(BuildContext context) {
final currentIndex = _currentIndex(location, items);
final cs = Theme.of(context).colorScheme;
// M3 Expressive: tonal surface container instead of a hard border divider.
return Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border(
right: BorderSide(
color: Theme.of(context).colorScheme.outlineVariant,
),
),
),
decoration: BoxDecoration(color: cs.surfaceContainerLow),
child: NavigationRail(
backgroundColor: Colors.transparent,
extended: extended,
selectedIndex: currentIndex,
onDestinationSelected: (value) {
items[value].onTap(context, onLogout: onLogout);
},
leading: const SizedBox.shrink(),
trailing: const SizedBox.shrink(),
leading: Padding(
padding: EdgeInsets.symmetric(
vertical: extended ? 12 : 8,
horizontal: extended ? 16 : 0,
),
child: Center(
child: Image.asset(
'assets/tasq_ico.png',
width: extended ? 48 : 40,
height: extended ? 48 : 40,
),
),
),
trailing: Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 16),
child: IconButton(
tooltip: 'Sign out',
onPressed: onLogout,
icon: const Icon(Icons.logout),
),
),
),
),
destinations: [
for (final item in items)
NavigationRailDestination(
@@ -256,6 +279,8 @@ class _NotificationBell extends ConsumerWidget {
}
}
/// M3 Expressive shell background — uses a subtle tonal surface tint
/// rather than a gradient to create the organic, seed-colored feel.
class _ShellBackground extends StatelessWidget {
const _ShellBackground({required this.child});
@@ -263,17 +288,8 @@ class _ShellBackground extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Theme.of(context).colorScheme.surface,
Theme.of(context).colorScheme.surfaceContainerLowest,
],
),
),
return ColoredBox(
color: Theme.of(context).scaffoldBackgroundColor,
child: child,
);
}
@@ -531,9 +547,8 @@ Future<void> _showOverflowSheet(
List<NavItem> items,
VoidCallback onLogout,
) async {
await showModalBottomSheet<void>(
await m3ShowBottomSheet<void>(
context: context,
showDragHandle: true,
builder: (context) {
return SafeArea(
child: ListView(