Switch to Inter, round the corners softly and make the nav switch tabs
Inter replaces General Sans (Inter Display for titles and big numbers). Corners come back from before the squaring commit, scaled to a soft set (16 cards, 14 buttons, 12 rows, 8 chips) and applied to the screens built since. The nav is now a cluster of round frosted buttons that switches Today, Routines, Goals, Groups and Progress in place inside Home: no pushed pages, no back arrows, and Android back returns to Today. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KhQh614DMeYoQEH9SoMRUw
This commit is contained in:
@@ -119,8 +119,8 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
||||
final GroupMessage? older = index + 1 < newestFirst.length
|
||||
? newestFirst[index + 1]
|
||||
: null;
|
||||
final bool newDay = older == null ||
|
||||
!_sameDay(older.sentAt, message.sentAt);
|
||||
final bool newDay =
|
||||
older == null || !_sameDay(older.sentAt, message.sentAt);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -145,6 +145,7 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
@@ -198,9 +199,12 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
color: colorWhite.withValues(alpha: 0.7),
|
||||
child: text(_dayName(moment), 11, TextType.Medium,
|
||||
color: colorInkSoft),
|
||||
decoration: BoxDecoration(
|
||||
color: colorWhite.withValues(alpha: 0.7),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child:
|
||||
text(_dayName(moment), 11, TextType.Medium, color: colorInkSoft),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -241,7 +245,10 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
||||
alignment: Alignment.centerRight,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(14, 11, 14, 10),
|
||||
color: colorNight,
|
||||
decoration: BoxDecoration(
|
||||
color: colorNight,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -273,6 +280,7 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(14, 10, 14, 10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
color: colorWhite,
|
||||
border: Border.all(color: colorGlassBorder),
|
||||
),
|
||||
@@ -308,6 +316,7 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 14, 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
color: checkIn ? colorStandingGoodBg : colorOverdueWarmEnd,
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
@@ -357,7 +366,10 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
||||
|
||||
Widget _composer() {
|
||||
return Container(
|
||||
color: colorWhite,
|
||||
decoration: BoxDecoration(
|
||||
color: colorWhite,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
0, 10, 0, MediaQuery.of(context).padding.bottom + 10),
|
||||
child: Column(
|
||||
@@ -375,7 +387,10 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||
alignment: Alignment.center,
|
||||
color: colorAmbientMid,
|
||||
decoration: BoxDecoration(
|
||||
color: colorAmbientMid,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: text(_quickReplies[index], 13, TextType.Regular,
|
||||
color: colorInk),
|
||||
),
|
||||
@@ -396,7 +411,10 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
||||
child: Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
color: colorNight,
|
||||
decoration: BoxDecoration(
|
||||
color: colorNight,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Icon(CupertinoIcons.paperplane_fill,
|
||||
size: 20, color: colorLime),
|
||||
),
|
||||
|
||||
@@ -137,8 +137,8 @@ class GroupDetailState extends State<GroupDetail>
|
||||
_amount.text = formatAmount(value).replaceAll(",", "");
|
||||
}
|
||||
|
||||
void _onSubmitLog(BuildContext sheetContext, GroupGoal goal,
|
||||
GroupObjective objective) {
|
||||
void _onSubmitLog(
|
||||
BuildContext sheetContext, GroupGoal goal, GroupObjective objective) {
|
||||
final double amount = double.tryParse(_amount.text.trim()) ?? 0;
|
||||
if (amount <= 0) {
|
||||
return;
|
||||
@@ -258,7 +258,10 @@ class GroupDetailState extends State<GroupDetail>
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
color: colorAmbientMid,
|
||||
decoration: BoxDecoration(
|
||||
color: colorAmbientMid,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -292,6 +295,7 @@ class GroupDetailState extends State<GroupDetail>
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(20, 18, 16, 18),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
@@ -317,16 +321,18 @@ class GroupDetailState extends State<GroupDetail>
|
||||
Icon(CupertinoIcons.chat_bubble_2_fill,
|
||||
size: 16, color: colorLime),
|
||||
const SizedBox(width: 8),
|
||||
text("Group chat", 14, TextType.Medium,
|
||||
color: colorLime),
|
||||
text("Group chat", 14, TextType.Medium, color: colorLime),
|
||||
if (_group.unread > 0) ...[
|
||||
const SizedBox(width: 10),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 7, vertical: 3),
|
||||
color: colorLime,
|
||||
child: text("${_group.unread} new", 10,
|
||||
TextType.Medium,
|
||||
decoration: BoxDecoration(
|
||||
color: colorLime,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
child: text(
|
||||
"${_group.unread} new", 10, TextType.Medium,
|
||||
color: colorNight),
|
||||
),
|
||||
],
|
||||
@@ -351,9 +357,12 @@ class GroupDetailState extends State<GroupDetail>
|
||||
Container(
|
||||
width: 46,
|
||||
height: 46,
|
||||
color: colorWhite.withValues(alpha: 0.12),
|
||||
child: Icon(CupertinoIcons.arrow_right,
|
||||
size: 20, color: colorWhite),
|
||||
decoration: BoxDecoration(
|
||||
color: colorWhite.withValues(alpha: 0.12),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child:
|
||||
Icon(CupertinoIcons.arrow_right, size: 20, color: colorWhite),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -409,7 +418,9 @@ class GroupDetailState extends State<GroupDetail>
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
meter(goal.progress,
|
||||
fill: tone, track: tone.withValues(alpha: 0.14), height: 6),
|
||||
fill: tone,
|
||||
track: tone.withValues(alpha: 0.14),
|
||||
height: 6),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
@@ -426,6 +437,7 @@ class GroupDetailState extends State<GroupDetail>
|
||||
return Container(
|
||||
padding: const EdgeInsets.fromLTRB(20, 14, 14, 16),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
border: Border(top: BorderSide(color: colorBorder)),
|
||||
),
|
||||
child: Row(
|
||||
@@ -472,7 +484,10 @@ class GroupDetailState extends State<GroupDetail>
|
||||
onTap: () => _onLog(goal, objective),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
color: colorNight,
|
||||
decoration: BoxDecoration(
|
||||
color: colorNight,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@@ -560,7 +575,10 @@ class GroupDetailState extends State<GroupDetail>
|
||||
child: Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
color: nudged ? colorAmbientMid : colorNight,
|
||||
decoration: BoxDecoration(
|
||||
color: nudged ? colorAmbientMid : colorNight,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Icon(
|
||||
nudged
|
||||
? CupertinoIcons.checkmark
|
||||
@@ -579,13 +597,13 @@ class GroupDetailState extends State<GroupDetail>
|
||||
|
||||
/// Log progress: an amount (with one-tap presets that suit the unit) and
|
||||
/// an optional note that rides along into the chat.
|
||||
Widget _logSheet(BuildContext sheetContext, GroupGoal goal,
|
||||
GroupObjective objective) {
|
||||
Widget _logSheet(
|
||||
BuildContext sheetContext, GroupGoal goal, GroupObjective objective) {
|
||||
final List<double> presets = _presets(objective.unit);
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(20, 16, 20,
|
||||
MediaQuery.of(sheetContext).viewInsets.bottom + 28),
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
20, 16, 20, MediaQuery.of(sheetContext).viewInsets.bottom + 28),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -612,9 +630,12 @@ class GroupDetailState extends State<GroupDetail>
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
alignment: Alignment.center,
|
||||
color: colorAmbientMid,
|
||||
child: text("+${formatAmount(preset)}", 15,
|
||||
TextType.Medium,
|
||||
decoration: BoxDecoration(
|
||||
color: colorAmbientMid,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: text(
|
||||
"+${formatAmount(preset)}", 15, TextType.Medium,
|
||||
color: colorInk),
|
||||
),
|
||||
),
|
||||
@@ -636,8 +657,8 @@ class GroupDetailState extends State<GroupDetail>
|
||||
const SizedBox(height: 14),
|
||||
inputField("Note", _note, hint: "Optional: posted with the check-in"),
|
||||
const SizedBox(height: 22),
|
||||
roundedCornerButton("Log it",
|
||||
() => _onSubmitLog(sheetContext, goal, objective),
|
||||
roundedCornerButton(
|
||||
"Log it", () => _onSubmitLog(sheetContext, goal, objective),
|
||||
icon: CupertinoIcons.checkmark),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -99,7 +99,8 @@ class GroupEditorState extends State<GroupEditor>
|
||||
}
|
||||
|
||||
if (_joining) {
|
||||
_model?.join(JoinGroupRequest(inviteCode: _code.text.trim().toUpperCase()));
|
||||
_model
|
||||
?.join(JoinGroupRequest(inviteCode: _code.text.trim().toUpperCase()));
|
||||
} else {
|
||||
_model?.create(GroupRequest(
|
||||
name: _name.text.trim(),
|
||||
@@ -130,9 +131,8 @@ class GroupEditorState extends State<GroupEditor>
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
displayTitle(_joining
|
||||
? "Got a code?"
|
||||
: "Who's keeping\nyou honest?"),
|
||||
displayTitle(
|
||||
_joining ? "Got a code?" : "Who's keeping\nyou honest?"),
|
||||
const SizedBox(height: 22),
|
||||
segmentedSelector<String>(
|
||||
options: const <String>["Create", "Join"],
|
||||
@@ -221,6 +221,7 @@ class GroupEditorState extends State<GroupEditor>
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
@@ -237,8 +238,7 @@ class GroupEditorState extends State<GroupEditor>
|
||||
size: 16, color: colorWhite.withValues(alpha: 0.7)),
|
||||
const SizedBox(width: 8),
|
||||
text("1 MEMBER", 9, TextType.Bold,
|
||||
color: colorWhite.withValues(alpha: 0.6),
|
||||
letterSpacing: 1.2),
|
||||
color: colorWhite.withValues(alpha: 0.6), letterSpacing: 1.2),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
@@ -266,6 +266,7 @@ class GroupEditorState extends State<GroupEditor>
|
||||
height: 40,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
color: colour,
|
||||
border: Border.all(
|
||||
color: selected ? colorWhite : Colors.transparent,
|
||||
|
||||
@@ -195,8 +195,7 @@ class GroupGoalEditorState extends State<GroupGoalEditor>
|
||||
maxLines: 2,
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
sectionBreak("Objectives",
|
||||
caption: "what everyone logs toward"),
|
||||
sectionBreak("Objectives", caption: "what everyone logs toward"),
|
||||
text(
|
||||
"Break the goal into numbers people can add to: kilometres, sessions, pages, hours. Each member logs their share and the totals add up.",
|
||||
12,
|
||||
@@ -288,7 +287,8 @@ class GroupGoalEditorState extends State<GroupGoalEditor>
|
||||
keyboard: const TextInputType.numberWithOptions(
|
||||
decimal: true),
|
||||
formatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.allow(RegExp(r"[0-9.]")),
|
||||
FilteringTextInputFormatter.allow(
|
||||
RegExp(r"[0-9.]")),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -3,7 +3,11 @@ import 'package:flutter/material.dart';
|
||||
import 'GroupsState.dart';
|
||||
|
||||
class Groups extends StatefulWidget {
|
||||
const Groups({super.key});
|
||||
/// Shown as a tab inside Home: no back arrow, and room at the bottom
|
||||
/// for the nav.
|
||||
final bool tab;
|
||||
|
||||
const Groups({super.key, this.tab = false});
|
||||
|
||||
@override
|
||||
State<Groups> createState() => GroupsState();
|
||||
|
||||
@@ -86,7 +86,8 @@ class GroupsState extends State<Groups> implements ConnectGroups {
|
||||
return Sheet(
|
||||
eyebrow: "Grounded",
|
||||
title: "Groups",
|
||||
onBack: _onBack,
|
||||
onBack: widget.tab ? null : _onBack,
|
||||
bottomSpace: widget.tab ? 130 : 40,
|
||||
action: chromeAction(CupertinoIcons.add, _onNewGroup),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -133,6 +134,7 @@ class GroupsState extends State<Groups> implements ConnectGroups {
|
||||
height: 250,
|
||||
margin: const EdgeInsets.only(bottom: 14),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Color(0x262A3A30),
|
||||
@@ -141,6 +143,7 @@ class GroupsState extends State<Groups> implements ConnectGroups {
|
||||
),
|
||||
],
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
@@ -175,7 +178,10 @@ class GroupsState extends State<Groups> implements ConnectGroups {
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10, vertical: 6),
|
||||
color: colorLime,
|
||||
decoration: BoxDecoration(
|
||||
color: colorLime,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
||||
@@ -23,7 +23,8 @@ class ViewGroupChat extends ParentViewModel {
|
||||
|
||||
connection.onMessagesLoaded(getGroupMessageList(response.data));
|
||||
} catch (e) {
|
||||
handleError(e, () => loadMessages(groupId), () => dismissError(), "Retry");
|
||||
handleError(
|
||||
e, () => loadMessages(groupId), () => dismissError(), "Retry");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ class ViewGroupDetail extends ParentViewModel {
|
||||
|
||||
connection.onProgressLogged(Group.fromJson(response.data), summary);
|
||||
} catch (e) {
|
||||
handleError(e, () => logProgress(request, summary),
|
||||
() => dismissError(), "Retry");
|
||||
handleError(e, () => logProgress(request, summary), () => dismissError(),
|
||||
"Retry");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user