feat(ui): mobile UI v2 — contrast, avatars, room-list density, compose FAB, gradient login (v1.8.0+12)
From a 7-agent design workflow (3 lenses judged + synthesised). Shipped the high-impact/low-risk tier: - message bubbles: self #1265ED (WCAG AA pass), incoming #1C2452 + hairline - deterministic per-user avatar colours (8-hue brand palette) - room tile: brighter unread preview, compact density, aligned timestamp - FAB -> New message (thumb zone), explore -> AppBar - login + welcome brand gradient, card outlines - input maxLines cap, bubble max-width viewport fraction, AppBar hairline All verified on production build in headless Chromium at mobile size. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
// Version: 1.1.0 | Created: 2026-04-01 | Updated: 2026-04-02
|
||||
// Individual room list tile. Kept under 100 lines.
|
||||
// Version: 1.2.0 | Created: 2026-04-01 | Updated: 2026-07-04
|
||||
// Individual room list tile. Kept under 120 lines.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:timeago/timeago.dart' as timeago;
|
||||
|
||||
import '../../../app/theme.dart';
|
||||
import '../../../shared/widgets/matrix_avatar.dart';
|
||||
import '../domain/room_model.dart';
|
||||
|
||||
@@ -19,6 +20,8 @@ class RoomTile extends StatelessWidget {
|
||||
final hasUnread = room.unreadCount > 0;
|
||||
|
||||
return ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
leading: MatrixAvatar(
|
||||
name: room.displayName,
|
||||
avatarUrl: room.avatarUrl,
|
||||
@@ -38,12 +41,15 @@ class RoomTile extends StatelessWidget {
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface.withAlpha(153),
|
||||
color: hasUnread
|
||||
? theme.colorScheme.onSurface.withAlpha(230)
|
||||
: M8Colours.subtleText,
|
||||
fontWeight: hasUnread ? FontWeight.w500 : FontWeight.normal,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
trailing: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (room.lastActivityAt != null)
|
||||
@@ -52,13 +58,14 @@ class RoomTile extends StatelessWidget {
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: hasUnread
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.onSurface.withAlpha(102),
|
||||
: M8Colours.subtleText,
|
||||
),
|
||||
),
|
||||
if (hasUnread) ...[
|
||||
const SizedBox(height: 4),
|
||||
_UnreadBadge(count: room.unreadCount),
|
||||
],
|
||||
] else
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
onTap: onTap,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Version: 1.6.0 | Created: 2026-04-01 | Updated: 2026-07-04
|
||||
// Version: 1.7.0 | Created: 2026-04-01 | Updated: 2026-07-04
|
||||
// Main rooms list screen with bottom navigation.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../app/theme.dart';
|
||||
import '../../../core/network/matrix_client.dart';
|
||||
import '../../help/presentation/help_tab.dart';
|
||||
import '../../panic/presentation/panic_button.dart';
|
||||
@@ -123,19 +124,20 @@ class _RoomsScreenState extends ConsumerState<RoomsScreen> {
|
||||
),
|
||||
if (!_searchActive)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit_square),
|
||||
tooltip: 'New message',
|
||||
onPressed: () => showUserSearchDialog(context),
|
||||
icon: const Icon(Icons.explore_outlined),
|
||||
tooltip: 'Find rooms',
|
||||
onPressed: () => showPublicRoomsDialog(context),
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
body: _buildBody(),
|
||||
floatingActionButton: _selectedIndex == 0 && !_searchActive
|
||||
? FloatingActionButton(
|
||||
onPressed: () => showPublicRoomsDialog(context),
|
||||
tooltip: 'Find rooms',
|
||||
child: const Icon(Icons.explore_outlined),
|
||||
? FloatingActionButton.extended(
|
||||
onPressed: () => showUserSearchDialog(context),
|
||||
tooltip: 'New message',
|
||||
icon: const Icon(Icons.edit_square),
|
||||
label: const Text('New message'),
|
||||
)
|
||||
: null,
|
||||
bottomNavigationBar: NavigationBar(
|
||||
@@ -253,16 +255,16 @@ class _EmptyRoomsState extends StatelessWidget {
|
||||
Text(
|
||||
'No rooms yet',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurface.withAlpha(153),
|
||||
color: M8Colours.subtleText,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Use the pencil icon above to message someone, or find '
|
||||
'Tap the New message button to message someone, or find '
|
||||
'a public room to join.',
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface.withAlpha(102),
|
||||
color: M8Colours.subtleText,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
Reference in New Issue
Block a user