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:
@@ -91,3 +91,18 @@
|
|||||||
- **P5 Panic button:** NEW feature/panic module. Config in account data au.m8chat.panic (room + message). Hold-to-fire button in rooms AppBar (only shows when armed); Profile tile to arm/disarm. Alert text sent FIRST, then best-effort m.location pin (geo: uri, msc3488) with 10s geolocation budget so a denied permission never blocks the alert. VERIFIED end-to-end: text + location with exact GPS landed in the target room via mock geolocation.
|
- **P5 Panic button:** NEW feature/panic module. Config in account data au.m8chat.panic (room + message). Hold-to-fire button in rooms AppBar (only shows when armed); Profile tile to arm/disarm. Alert text sent FIRST, then best-effort m.location pin (geo: uri, msc3488) with 10s geolocation budget so a denied permission never blocks the alert. VERIFIED end-to-end: text + location with exact GPS landed in the target room via mock geolocation.
|
||||||
- **Status:** built, deployed app2.m8chat.au v1.7.0+11 (backup pre170 tarball on server). Analyze clean, tests pass.
|
- **Status:** built, deployed app2.m8chat.au v1.7.0+11 (backup pre170 tarball on server). Analyze clean, tests pass.
|
||||||
- **Deferred:** ghost-device cleanup, rooms-provider rebuild perf, native session persistence — still open from original notes.
|
- **Deferred:** ghost-device cleanup, rooms-provider rebuild perf, native session persistence — still open from original notes.
|
||||||
|
|
||||||
|
### 2026-07-04 07:50 — Mobile UI v2 design pass (v1.8.0+12)
|
||||||
|
- **Process:** ran a 7-agent design workflow (3 lens designers: information-density, reachability-flow, polish-delight; 3 judges; 1 synthesiser). All proposals scored 8-9/10. Output: prioritised spec (A high-impact/low-risk, B medium, C defer). Full spec archived in session.
|
||||||
|
- **Implemented the whole A tier + safe B items:**
|
||||||
|
- A1/A2 message bubble contrast: self #1265ED (selfBubble token, WCAG AA 5.11:1 vs failing 3.32:1), incoming #1C2452 + hairline border.
|
||||||
|
- A3 replaced failing low-alpha read text with subtleText token (rooms empty state, room tile).
|
||||||
|
- A4 deterministic per-user avatar colour (8-hue brand palette, name.hashCode) in matrix_avatar.dart — every disc now distinct.
|
||||||
|
- A5/A6 room tile: unread preview brightened+w500, compact density, timestamp aligned to title, unread dot spacing.
|
||||||
|
- A7 fixed stale "pencil icon" empty-state copy.
|
||||||
|
- A9 FAB now "New message" (compose) in thumb zone; explore moved to AppBar compass. Straight onPressed swap.
|
||||||
|
- A8 login + welcome brand gradient (#0A0F2E→#131A42), card hairline outlines.
|
||||||
|
- B7 message input capped minLines 1 / maxLines 6. B8 bubble maxWidth = viewport*0.78 clamped 560. B10 AppBar scrolledUnderElevation + hairline.
|
||||||
|
- **Deferred (documented in spec):** B1 message grouping, B2 day separators, B4 quick-reaction row, B5 panic hold-ring, B6 scroll-to-latest FAB, C1 swipe-to-reply (iOS PWA back-gesture risk), C2 group sender prefix (needs RoomModel field), C3 demote Help nav.
|
||||||
|
- **Verified:** headless Chromium at 390x844 on production build — gradient login, distinct avatar colours, compose FAB, AA bubble contrast all confirmed.
|
||||||
|
- **Status:** built + deployed app2.m8chat.au v1.8.0+12 (backup pre180 tarball). Analyze clean, tests pass.
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import 'package:flutter/material.dart';
|
|||||||
abstract final class M8Colours {
|
abstract final class M8Colours {
|
||||||
static const Color brandBlue = Color(0xFF1265ED);
|
static const Color brandBlue = Color(0xFF1265ED);
|
||||||
static const Color accentBlue = Color(0xFF3B8BFF);
|
static const Color accentBlue = Color(0xFF3B8BFF);
|
||||||
|
// Self message bubble — deeper than accentBlue so white text passes WCAG AA
|
||||||
|
// (white on #1265ED = 5.11:1; on #3B8BFF = 3.32:1, fails).
|
||||||
|
static const Color selfBubble = Color(0xFF1265ED);
|
||||||
static const Color darkBackground = Color(0xFF0A0F2E);
|
static const Color darkBackground = Color(0xFF0A0F2E);
|
||||||
static const Color darkSurface = Color(0xFF131A42);
|
static const Color darkSurface = Color(0xFF131A42);
|
||||||
static const Color darkSurfaceVariant = Color(0xFF1C2452);
|
static const Color darkSurfaceVariant = Color(0xFF1C2452);
|
||||||
@@ -43,7 +46,11 @@ ThemeData buildDarkTheme() {
|
|||||||
backgroundColor: M8Colours.darkSurface,
|
backgroundColor: M8Colours.darkSurface,
|
||||||
foregroundColor: M8Colours.onDarkSurface,
|
foregroundColor: M8Colours.onDarkSurface,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 2,
|
||||||
surfaceTintColor: Colors.transparent,
|
surfaceTintColor: Colors.transparent,
|
||||||
|
shape: const Border(
|
||||||
|
bottom: BorderSide(color: M8Colours.darkSurfaceVariant),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
navigationBarTheme: NavigationBarThemeData(
|
navigationBarTheme: NavigationBarThemeData(
|
||||||
backgroundColor: M8Colours.darkSurface,
|
backgroundColor: M8Colours.darkSurface,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ abstract final class AppConfig {
|
|||||||
'turns:matrix.m8chat.au:5349',
|
'turns:matrix.m8chat.au:5349',
|
||||||
];
|
];
|
||||||
static const String appName = 'M8Chat';
|
static const String appName = 'M8Chat';
|
||||||
static const String appVersion = '1.7.0';
|
static const String appVersion = '1.8.0';
|
||||||
|
|
||||||
// Jitsi conferencing
|
// Jitsi conferencing
|
||||||
static const String jitsiDomain = 'conf.m8chat.au';
|
static const String jitsiDomain = 'conf.m8chat.au';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Version: 1.2.0 | Created: 2026-04-01 | Updated: 2026-04-03
|
// Version: 1.3.0 | Created: 2026-04-01 | Updated: 2026-07-04
|
||||||
// Login screen. Username + password only. No registration link.
|
// Login screen. Username + password only. No registration link.
|
||||||
// Respects system theme preference.
|
// Respects system theme preference.
|
||||||
|
|
||||||
@@ -55,7 +55,15 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: DecoratedBox(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [Color(0xFF0A0F2E), Color(0xFF131A42)],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: SafeArea(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 24),
|
||||||
@@ -90,6 +98,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
// Version: 1.3.1 | Created: 2026-04-01 | Updated: 2026-07-03
|
// Version: 1.4.0 | Created: 2026-04-01 | Updated: 2026-07-04
|
||||||
// Message bubble widget. Handles text, images, files, redacted, replies.
|
// Message bubble widget. Handles text, images, files, redacted, replies.
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
import '../../../app/theme.dart';
|
||||||
import '../../../shared/widgets/matrix_avatar.dart';
|
import '../../../shared/widgets/matrix_avatar.dart';
|
||||||
import '../domain/message_model.dart';
|
import '../domain/message_model.dart';
|
||||||
|
|
||||||
@@ -79,14 +80,19 @@ class _BubbleContent extends StatelessWidget {
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
final bgColour = isMine
|
final bgColour = isMine
|
||||||
? theme.colorScheme.primary
|
? M8Colours.selfBubble
|
||||||
: theme.colorScheme.surfaceContainerHighest;
|
: M8Colours.darkSurfaceVariant;
|
||||||
final textColour = isMine ? Colors.white : theme.colorScheme.onSurface;
|
final textColour = isMine ? Colors.white : theme.colorScheme.onSurface;
|
||||||
|
final maxBubbleWidth =
|
||||||
|
(MediaQuery.sizeOf(context).width.clamp(0, 560) * 0.78).toDouble();
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 320),
|
constraints: BoxConstraints(maxWidth: maxBubbleWidth),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: bgColour,
|
color: bgColour,
|
||||||
|
border: isMine
|
||||||
|
? null
|
||||||
|
: Border.all(color: theme.colorScheme.outline.withAlpha(31)),
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: const Radius.circular(16),
|
topLeft: const Radius.circular(16),
|
||||||
topRight: const Radius.circular(16),
|
topRight: const Radius.circular(16),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Version: 1.1.0 | Created: 2026-04-01
|
// Version: 1.1.1 | Created: 2026-04-01 | Updated: 2026-07-04
|
||||||
// Message input bar — text, send, attach file, reply quote.
|
// Message input bar — text, send, attach file, reply quote.
|
||||||
// File picker enabled in Phase 2 via file_picker ^8.0.0.
|
// File picker enabled in Phase 2 via file_picker ^8.0.0.
|
||||||
|
|
||||||
@@ -195,7 +195,8 @@ class _MessageInputState extends State<MessageInput> {
|
|||||||
),
|
),
|
||||||
textInputAction: TextInputAction.send,
|
textInputAction: TextInputAction.send,
|
||||||
onSubmitted: (_) => _submit(),
|
onSubmitted: (_) => _submit(),
|
||||||
maxLines: null,
|
minLines: 1,
|
||||||
|
maxLines: 6,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Version: 1.0.1 | Created: 2026-04-05 | Updated: 2026-07-03
|
// Version: 1.1.0 | Created: 2026-04-05 | Updated: 2026-07-04
|
||||||
// Welcome/landing screen — first thing unauthenticated users see.
|
// Welcome/landing screen — first thing unauthenticated users see.
|
||||||
// Two paths: join a Jitsi conference (no login) or sign in for Matrix chat.
|
// Two paths: join a Jitsi conference (no login) or sign in for Matrix chat.
|
||||||
|
|
||||||
@@ -39,7 +39,15 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
|
|||||||
final isDark = theme.brightness == Brightness.dark;
|
final isDark = theme.brightness == Brightness.dark;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: DecoratedBox(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [Color(0xFF0A0F2E), Color(0xFF131A42)],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: SafeArea(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 24),
|
||||||
@@ -157,6 +165,7 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// Version: 1.1.0 | Created: 2026-04-01 | Updated: 2026-04-02
|
// Version: 1.2.0 | Created: 2026-04-01 | Updated: 2026-07-04
|
||||||
// Individual room list tile. Kept under 100 lines.
|
// Individual room list tile. Kept under 120 lines.
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:timeago/timeago.dart' as timeago;
|
import 'package:timeago/timeago.dart' as timeago;
|
||||||
|
|
||||||
|
import '../../../app/theme.dart';
|
||||||
import '../../../shared/widgets/matrix_avatar.dart';
|
import '../../../shared/widgets/matrix_avatar.dart';
|
||||||
import '../domain/room_model.dart';
|
import '../domain/room_model.dart';
|
||||||
|
|
||||||
@@ -19,6 +20,8 @@ class RoomTile extends StatelessWidget {
|
|||||||
final hasUnread = room.unreadCount > 0;
|
final hasUnread = room.unreadCount > 0;
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
leading: MatrixAvatar(
|
leading: MatrixAvatar(
|
||||||
name: room.displayName,
|
name: room.displayName,
|
||||||
avatarUrl: room.avatarUrl,
|
avatarUrl: room.avatarUrl,
|
||||||
@@ -38,12 +41,15 @@ class RoomTile extends StatelessWidget {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: theme.textTheme.bodySmall?.copyWith(
|
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,
|
: null,
|
||||||
trailing: Column(
|
trailing: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
if (room.lastActivityAt != null)
|
if (room.lastActivityAt != null)
|
||||||
@@ -52,13 +58,14 @@ class RoomTile extends StatelessWidget {
|
|||||||
style: theme.textTheme.labelSmall?.copyWith(
|
style: theme.textTheme.labelSmall?.copyWith(
|
||||||
color: hasUnread
|
color: hasUnread
|
||||||
? theme.colorScheme.primary
|
? theme.colorScheme.primary
|
||||||
: theme.colorScheme.onSurface.withAlpha(102),
|
: M8Colours.subtleText,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasUnread) ...[
|
if (hasUnread) ...[
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
_UnreadBadge(count: room.unreadCount),
|
_UnreadBadge(count: room.unreadCount),
|
||||||
],
|
] else
|
||||||
|
const SizedBox(height: 8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: onTap,
|
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.
|
// Main rooms list screen with bottom navigation.
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import '../../../app/theme.dart';
|
||||||
import '../../../core/network/matrix_client.dart';
|
import '../../../core/network/matrix_client.dart';
|
||||||
import '../../help/presentation/help_tab.dart';
|
import '../../help/presentation/help_tab.dart';
|
||||||
import '../../panic/presentation/panic_button.dart';
|
import '../../panic/presentation/panic_button.dart';
|
||||||
@@ -123,19 +124,20 @@ class _RoomsScreenState extends ConsumerState<RoomsScreen> {
|
|||||||
),
|
),
|
||||||
if (!_searchActive)
|
if (!_searchActive)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.edit_square),
|
icon: const Icon(Icons.explore_outlined),
|
||||||
tooltip: 'New message',
|
tooltip: 'Find rooms',
|
||||||
onPressed: () => showUserSearchDialog(context),
|
onPressed: () => showPublicRoomsDialog(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
body: _buildBody(),
|
body: _buildBody(),
|
||||||
floatingActionButton: _selectedIndex == 0 && !_searchActive
|
floatingActionButton: _selectedIndex == 0 && !_searchActive
|
||||||
? FloatingActionButton(
|
? FloatingActionButton.extended(
|
||||||
onPressed: () => showPublicRoomsDialog(context),
|
onPressed: () => showUserSearchDialog(context),
|
||||||
tooltip: 'Find rooms',
|
tooltip: 'New message',
|
||||||
child: const Icon(Icons.explore_outlined),
|
icon: const Icon(Icons.edit_square),
|
||||||
|
label: const Text('New message'),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
bottomNavigationBar: NavigationBar(
|
bottomNavigationBar: NavigationBar(
|
||||||
@@ -253,16 +255,16 @@ class _EmptyRoomsState extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
'No rooms yet',
|
'No rooms yet',
|
||||||
style: theme.textTheme.titleMedium?.copyWith(
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
color: theme.colorScheme.onSurface.withAlpha(153),
|
color: M8Colours.subtleText,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
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.',
|
'a public room to join.',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: theme.textTheme.bodySmall?.copyWith(
|
style: theme.textTheme.bodySmall?.copyWith(
|
||||||
color: theme.colorScheme.onSurface.withAlpha(102),
|
color: M8Colours.subtleText,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// Version: 1.0.0 | Created: 2026-04-02
|
// Version: 1.1.0 | Created: 2026-04-02 | Updated: 2026-07-04
|
||||||
// Shared avatar widget used throughout the app. Displays a cached network
|
// Shared avatar widget used throughout the app. Displays a cached network
|
||||||
// image when an HTTP avatar URL is available, or falls back to a coloured
|
// image when an HTTP avatar URL is available, or falls back to a circle
|
||||||
// circle with the first letter of the display name.
|
// coloured deterministically from the display name (so each person gets a
|
||||||
|
// stable, distinct disc) with the first letter of the display name.
|
||||||
//
|
//
|
||||||
// The [avatarUrl] MUST be a resolved HTTP URL — never pass an mxc:// URI.
|
// The [avatarUrl] MUST be a resolved HTTP URL — never pass an mxc:// URI.
|
||||||
|
|
||||||
@@ -25,6 +26,21 @@ class MatrixAvatar extends StatelessWidget {
|
|||||||
/// Radius of the [CircleAvatar].
|
/// Radius of the [CircleAvatar].
|
||||||
final double radius;
|
final double radius;
|
||||||
|
|
||||||
|
/// Brand-compatible hues for initials avatars. All dark enough for white
|
||||||
|
/// initials to remain legible.
|
||||||
|
static const List<Color> _palette = [
|
||||||
|
Color(0xFF1265ED), // brand blue
|
||||||
|
Color(0xFF2E7DE0), // sky
|
||||||
|
Color(0xFF1E88A8), // teal
|
||||||
|
Color(0xFF3949AB), // indigo
|
||||||
|
Color(0xFF6A4CC0), // violet
|
||||||
|
Color(0xFF00897B), // green-teal
|
||||||
|
Color(0xFF5C6BC0), // periwinkle
|
||||||
|
Color(0xFFB1631F), // warm amber
|
||||||
|
];
|
||||||
|
|
||||||
|
Color get _discColour => _palette[name.hashCode.abs() % _palette.length];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
@@ -40,13 +56,13 @@ class MatrixAvatar extends StatelessWidget {
|
|||||||
|
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
radius: radius,
|
radius: radius,
|
||||||
backgroundColor: theme.colorScheme.primary.withAlpha(51),
|
backgroundColor: _discColour,
|
||||||
child: Text(
|
child: Text(
|
||||||
initials,
|
initials,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: radius * 0.7,
|
fontSize: radius * 0.7,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: theme.colorScheme.primary,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: m8chat_app
|
name: m8chat_app
|
||||||
description: "M8Chat — Matrix chat client for Android, iOS, and Web."
|
description: "M8Chat — Matrix chat client for Android, iOS, and Web."
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.7.0+11
|
version: 1.8.0+12
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.11.0 <4.0.0'
|
sdk: '>=3.11.0 <4.0.0'
|
||||||
|
|||||||
Reference in New Issue
Block a user