Compare commits

...

2 Commits

Author SHA1 Message Date
alvocool
4548e1f164 Rebuild Home around a forest header with streak, score and water
Home now opens on a deep green header: greeting, a Today / Weekly toggle,
a seven-day streak with a leaf on every kept day, minutes held with a +
ring, plan thumbnails, the report-card letter as the score, and one bar per
commitment class (weekly view swaps in per-day bars and class rates). The
+ opens an "Add to today" sheet over the blurred day. A water tracker sits
below the header, kept on the device. The nav marks the selected tab as a
filled block and drops the side action button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KhQh614DMeYoQEH9SoMRUw
2026-11-02 20:58:41 +03:00
alvocool
9c858b8146 Square every corner, enlarge the main surfaces and add a to-do list
Rounded rectangles and round controls are now square across the app; only
status dots, glows, the sun, rings and the breathing circle stay round.
Cards, rows, thumbnails, featured cards, category tiles, the standing card
and the nav are larger. Home gets a quick To-do list kept on the device
(TodoItem in InternalMemory), seeded with sample items in mock mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KhQh614DMeYoQEH9SoMRUw
2026-11-02 14:50:31 +03:00
39 changed files with 1540 additions and 462 deletions

View File

@@ -111,7 +111,7 @@ status field. That's what makes honest history and excuse analysis possible.
See `design.md`. In short: sage-to-cream ambient backdrop, frosted glass
controls, painted `SceneArt` instead of photos, image-led cards, two-tone
greetings, night pills with a lime spark.
greetings, square night buttons with a lime spark, no rounded corners.
- Screens are built from `Sheet` (`designs/Shell.dart`); Home, Routines and
the routine player compose `AmbientBackground` + glass directly.

View File

@@ -4,7 +4,7 @@ Reference: an airy fitness-app look. Sage mist drifting into cream behind
everything (`AmbientBackground`), frosted glass for controls and floating
chrome (`GlassSurface`, `glassCircle`), ink type with a soft grey second
tone, painted landscapes instead of photography (`SceneArt`), a floating
glass tab bar with a dark round action button (`FloatingNav`), and one lime
glass tab bar with a dark square action button (`FloatingNav`), and one lime
spark on night-green for the primary control.
## Building blocks (`designs/`)
@@ -12,16 +12,16 @@ spark on night-green for the primary control.
| Piece | Use |
|---|---|
| `AmbientBackground` | The backdrop for every light screen. |
| `GlassSurface` / `glassCircle` | Frosted panels and round controls; `onDark` over a scene or tinted stage. |
| `GlassSurface` / `glassCircle` | Frosted panels and square controls; `onDark` over a scene or tinted stage. |
| `SceneArt` | Painted sky, sun and ridges with an optional figure glyph. Palettes come from `scenePalette(kind)`, `goalScene(accent)`, `classScene(class)`; seed with `sceneSeed(title)` so an item always wears the same picture. |
| `DotDigits` | The dotted clock in the routine player. |
| `FloatingNav` | Home's tab bar and action button. |
| `FloatingNav` | Home's tab bar; the selected tab is a filled night block. The side action button is optional. |
| `GroundedMark` | The logo, drawn — a lime sun on a ground line under two arcs. The launcher PNGs in `assets/icons/` were rendered from the same geometry. |
Cards are image-led where an item has a kind (routines, goals, tasks): scene on
top or as a thumbnail, white type over the shaded lower half, three light
numbers along the bottom. Greetings are two-tone: Medium ink, then Regular
`colorInkSoft`. Primary buttons are night pills with a lime icon.
`colorInkSoft`. Primary buttons are square night bars with a lime icon.
`Sheet` is light (ambient) by default; passing `chrome`, `banner` or
`scene` gives a tinted stage with white type. Home, Routines and the player
@@ -45,7 +45,7 @@ directly above a large dark value. Used everywhere numbers appear.
## Geometry
- Sheet radius `28`, card radius `16`, chip radius `999`, control radius `12`.
- No rounded corners anywhere: sheets, cards, chips, buttons, fields, tiles and portraits are all square (`BorderRadius.zero`). Only small status dots, glows, the sun, progress rings and the breathing circle stay round.
- Screen padding `20` horizontal. Card padding `16`. Section gap `28`.
- Hairlines at `colorDivider` (black 7%), borders at `colorBorder` (black 8%).
- One soft shadow only: `0 4 18 rgba(0,0,0,0.03)`. No coloured glows.
@@ -69,7 +69,7 @@ the standing is legible without reading the word.
## Structure
Every screen is: black `headerBar` (back, title, action) → white sheet with a
`28` top radius → `sectionHeader` (label + light display title) → content.
square top edge → `sectionHeader` (label + light display title) → content.
## Rules
@@ -78,3 +78,15 @@ Every screen is: black `headerBar` (back, title, action) → white sheet with a
- Chips carry state, never actions.
- One primary button per screen; everything else is outlined or text.
- Empty states are plain. An empty overdue queue is good news, not a party.
## Home
Home opens on a deep forest header block (`colorHeaderTop` → `colorHeaderBottom`):
greeting and avatar, a Today / Weekly toggle, a seven-day streak panel ending
today (a leaf on every kept day, kept = 80%+), then the numbers. Today shows
minutes held against planned, a ring with + that opens the "Add to today"
sheet, the plan as thumbnails, the report-card letter as the Score, and one
bar per commitment class. Weekly swaps in the week's average, a bar per day
and the class completion rates. Below the block, on the light ambient
surface: the water tracker (drops of 250 ml, kept on the device), then the
rest of the day. Bars on the dark block use the `colorBar*` tokens.

View File

@@ -0,0 +1,27 @@
/// A quick to-do: no window, no class, no debt. Kept on the device so jotting
/// something down never waits on the network.
class TodoItem {
String id;
String title;
bool done;
TodoItem({required this.id, required this.title, this.done = false});
factory TodoItem.fromJson(Map<String, dynamic> json) {
return TodoItem(
id: json['id'] ?? '',
title: json['title'] ?? '',
done: json['done'] ?? false,
);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['title'] = title;
data['done'] = done;
return data;
}
}

View File

@@ -48,7 +48,7 @@ class TickBox extends StatelessWidget {
height: size,
decoration: BoxDecoration(
color: checked ? tone : Colors.transparent,
borderRadius: BorderRadius.circular(7),
borderRadius: BorderRadius.zero,
border: Border.all(
color: checked ? tone : colorGrey.withValues(alpha: 0.55),
width: 1.6,
@@ -202,7 +202,7 @@ Widget addTaskAffordance(
alignment: Alignment.center,
decoration: BoxDecoration(
color: tone.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(14),
borderRadius: BorderRadius.zero,
border: Border.all(color: tone.withValues(alpha: 0.25), width: 1),
),
child: Icon(Icons.add_rounded, size: 22, color: tone),

View File

@@ -32,7 +32,7 @@ Widget pill(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: background,
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
border: Border.all(color: foreground.withValues(alpha: 0.20), width: 1),
),
child: Row(
@@ -57,7 +57,7 @@ Widget pill(
/// The standard card surface.
Widget card({
required Widget child,
EdgeInsets padding = const EdgeInsets.all(16),
EdgeInsets padding = const EdgeInsets.all(20),
EdgeInsets margin = EdgeInsets.zero,
Color? background,
Color? borderColor,
@@ -70,7 +70,7 @@ Widget card({
margin: margin,
decoration: BoxDecoration(
color: background ?? colorCard,
borderRadius: BorderRadius.circular(radius),
borderRadius: BorderRadius.zero,
border: Border.all(color: borderColor ?? colorGlassBorder, width: 1),
boxShadow: const [
BoxShadow(
@@ -159,7 +159,6 @@ Widget emptyState(
height: 76,
decoration: BoxDecoration(
color: tone.withValues(alpha: 0.08),
shape: BoxShape.circle,
),
child: Icon(icon, size: 32, color: tone),
),
@@ -191,7 +190,7 @@ Widget meter(
: fraction;
return ClipRRect(
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
child: LinearProgressIndicator(
value: clamped,
minHeight: height,

View File

@@ -7,22 +7,23 @@ import 'text/Text.dart';
typedef NavEntry = ({IconData icon, String label, VoidCallback onTap});
/// The floating glass tab bar with the dark round action button beside it.
/// The floating tab bar: the selected tab is a filled night block, the rest
/// sit quietly on white. An optional dark action button can sit beside it.
class FloatingNav extends StatelessWidget {
final List<NavEntry> entries;
final int selected;
final IconData actionIcon;
final IconData? actionIcon;
final VoidCallback onAction;
final VoidCallback? onAction;
const FloatingNav({
super.key,
required this.entries,
required this.selected,
required this.actionIcon,
required this.onAction,
this.actionIcon,
this.onAction,
});
@override
@@ -36,7 +37,7 @@ class FloatingNav extends StatelessWidget {
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(34),
borderRadius: BorderRadius.zero,
boxShadow: const [
BoxShadow(
color: Color(0x1A1A1F1C),
@@ -46,7 +47,7 @@ class FloatingNav extends StatelessWidget {
],
),
child: GlassSurface(
radius: BorderRadius.circular(34),
radius: BorderRadius.zero,
tint: colorGlassStrong,
padding: const EdgeInsets.all(6),
child: Row(
@@ -58,14 +59,14 @@ class FloatingNav extends StatelessWidget {
),
),
),
if (onAction != null) ...[
const SizedBox(width: 10),
GestureDetector(
onTap: onAction,
child: Container(
width: 64,
height: 64,
width: 72,
height: 72,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const RadialGradient(
center: Alignment(-0.3, -0.4),
colors: <Color>[colorNightGlow, colorNight],
@@ -82,6 +83,7 @@ class FloatingNav extends StatelessWidget {
child: Icon(actionIcon, size: 24, color: colorLime),
),
),
],
],
),
),
@@ -95,10 +97,10 @@ class FloatingNav extends StatelessWidget {
child: AnimatedContainer(
duration: const Duration(milliseconds: 220),
curve: Curves.easeOutCubic,
padding: const EdgeInsets.symmetric(vertical: 9),
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: active ? colorWhite : colorWhite.withValues(alpha: 0.0),
borderRadius: BorderRadius.circular(28),
color: active ? colorHeaderBottom : colorWhite.withValues(alpha: 0.0),
borderRadius: BorderRadius.zero,
boxShadow: active
? const [
BoxShadow(
@@ -113,10 +115,10 @@ class FloatingNav extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Icon(entry.icon,
size: 19, color: active ? colorInk : colorInkSoft),
size: 23, color: active ? colorWhite : colorInkSoft),
const SizedBox(height: 4),
text(entry.label, 10, active ? TextType.Medium : TextType.Regular,
color: active ? colorInk : colorInkSoft, maxLines: 1),
color: active ? colorWhite : colorInkSoft, maxLines: 1),
],
),
),

View File

@@ -23,7 +23,7 @@ class GlassSurface extends StatelessWidget {
const GlassSurface({
super.key,
required this.child,
this.radius = const BorderRadius.all(Radius.circular(20)),
this.radius = const BorderRadius.all(Radius.zero),
this.padding = EdgeInsets.zero,
this.tint,
this.onDark = false,
@@ -60,7 +60,7 @@ class GlassSurface extends StatelessWidget {
Widget glassCircle(
IconData icon,
VoidCallback onTap, {
double size = 42,
double size = 50,
bool onDark = false,
bool dotted = false,
Color? dotColor,
@@ -73,7 +73,7 @@ Widget glassCircle(
children: [
GlassSurface(
onDark: onDark,
radius: BorderRadius.circular(size),
radius: BorderRadius.zero,
child: SizedBox(
width: size,
height: size,

View File

@@ -32,8 +32,8 @@ Future<Goal?> showGoalPicker(
decoration: const BoxDecoration(
color: colorSheetBackground,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
child: ListView(
@@ -93,7 +93,7 @@ Widget _goalOption(
padding: const EdgeInsets.fromLTRB(14, 13, 14, 13),
decoration: BoxDecoration(
color: colorWhite,
borderRadius: BorderRadius.circular(14),
borderRadius: BorderRadius.zero,
border: Border.all(
color: selected ? accent.withValues(alpha: 0.5) : colorBorder,
width: selected ? 1.5 : 1,

View File

@@ -136,8 +136,8 @@ class Sheet extends StatelessWidget {
decoration: BoxDecoration(
color: light ? colorFrost : colorPrimaryLight,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
clipBehavior: Clip.antiAlias,

View File

@@ -27,7 +27,7 @@ Widget roundedCornerButton(
disabledBackgroundColor: colorGrey.withValues(alpha: 0.4),
padding: EdgeInsets.symmetric(vertical: verticalPadding),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radius),
borderRadius: BorderRadius.zero,
),
),
child: Row(
@@ -65,7 +65,7 @@ Widget outlinedActionButton(
side: BorderSide(color: fg.withValues(alpha: 0.35), width: 1),
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radius),
borderRadius: BorderRadius.zero,
),
),
child: Row(
@@ -112,7 +112,7 @@ Widget iconButton(
alignment: Alignment.center,
decoration: BoxDecoration(
color: background ?? Colors.transparent,
borderRadius: BorderRadius.circular(radius),
borderRadius: BorderRadius.zero,
border: bordered
? Border.all(color: borderColor ?? colorBorder, width: 1)
: null,
@@ -161,7 +161,7 @@ Widget segmentedSelector<T>({
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: isSelected ? active : colorWhite,
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.zero,
border: Border.all(
color: isSelected ? active : colorBorder,
width: 1,

View File

@@ -17,7 +17,7 @@ Widget countStepper(
padding: const EdgeInsets.fromLTRB(14, 10, 8, 10),
decoration: BoxDecoration(
color: colorWhite,
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
border: Border.all(color: colorBorder, width: 1),
),
child: Row(
@@ -54,7 +54,7 @@ Widget _stepButton(IconData icon, VoidCallback? onTap) {
alignment: Alignment.center,
decoration: BoxDecoration(
color: enabled ? colorSecondary : colorInset,
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.zero,
),
child: Icon(icon,
size: 14, color: enabled ? colorPrimaryDark : colorGrey),

View File

@@ -54,19 +54,19 @@ Widget inputField(
contentPadding:
const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: colorBorder, width: 1),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: colorPrimaryDark, width: 1.4),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: colorNegative, width: 1),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: colorNegative, width: 1.4),
),
errorStyle: TextStyle(
@@ -80,6 +80,38 @@ Widget inputField(
);
}
/// The bare add-a-line field used inside a card: no label, no box of its
/// own; return or the trailing button submits.
Widget quickAddField(
TextEditingController controller,
String hint,
ValueChanged<String> onSubmitted, {
FocusNode? focusNode,
}) {
return TextField(
controller: controller,
focusNode: focusNode,
onSubmitted: onSubmitted,
textInputAction: TextInputAction.done,
style: TextStyle(
fontFamily: getTextType(TextType.Regular),
fontSize: 16,
color: colorInk,
),
decoration: InputDecoration(
hintText: hint,
hintStyle: TextStyle(
fontFamily: getTextType(TextType.Regular),
fontSize: 16,
color: colorInkSoft,
),
isDense: true,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 14),
),
);
}
/// The excuse field. Deliberately unadorned — no templates, no quick-picks, a
/// live character count that shows the minimum, because the friction is the
/// feature rather than an obstacle to route around.
@@ -131,19 +163,19 @@ Widget excuseField(
fillColor: colorWhite,
contentPadding: const EdgeInsets.all(14),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: colorBorder, width: 1),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: colorPrimaryDark, width: 1.4),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: colorNegative, width: 1),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: colorNegative, width: 1.4),
),
errorStyle: TextStyle(
@@ -180,7 +212,7 @@ Widget selectField(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 16),
decoration: BoxDecoration(
color: colorWhite,
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
border: Border.all(color: colorBorder, width: 1),
),
child: Row(

View File

@@ -21,6 +21,7 @@ import '../about/external/initial/SessionLogRequest.dart';
import '../about/external/initial/SickModeRequest.dart';
import '../about/external/initial/ToneRequest.dart';
import '../about/internal/application/CapacityProfile.dart';
import '../about/internal/application/TodoItem.dart';
import '../about/internal/application/MeDescription.dart';
import '../about/internal/application/Token.dart';
import '../about/internal/application/UserDetails.dart';
@@ -119,6 +120,18 @@ class AppDataManager implements DataManager {
@override
Future setAmnestySpent(int spent) => memory.setAmnestySpent(spent);
@override
Future<int> getWaterToday() => memory.getWaterToday();
@override
Future setWaterToday(int ml) => memory.setWaterToday(ml);
@override
Future<List<TodoItem>> getTodos() => memory.getTodos();
@override
Future setTodos(List<TodoItem> todos) => memory.setTodos(todos);
@override
Future<bool> showOnboarding() => memory.showOnboarding();

View File

@@ -3,6 +3,7 @@ import '../about/external/data/Program.dart';
import '../about/external/data/SystemResponse.dart';
import '../about/internal/application/CapacityProfile.dart';
import '../about/internal/application/MeDescription.dart';
import '../about/internal/application/TodoItem.dart';
import '../about/internal/application/Token.dart';
import '../about/internal/application/UserDetails.dart';
@@ -59,6 +60,16 @@ abstract class ConnectInternalMemory {
Future setAmnestySpent(int spent);
/// Water drunk today in ml; a new day reads as 0.
Future<int> getWaterToday();
Future setWaterToday(int ml);
/// The quick to-do list on Home.
Future<List<TodoItem>> getTodos();
Future setTodos(List<TodoItem> todos);
Future<bool> showOnboarding();
Future setOnboardingOption(bool value);

View File

@@ -8,6 +8,7 @@ import '../about/external/data/ResponseState.dart';
import '../about/external/data/SystemResponse.dart';
import '../about/internal/application/CapacityProfile.dart';
import '../about/internal/application/MeDescription.dart';
import '../about/internal/application/TodoItem.dart';
import '../about/internal/application/Token.dart';
import '../about/internal/application/UserDetails.dart';
import 'ConnectInternalMemory.dart';
@@ -40,6 +41,10 @@ class InternalMemory implements ConnectInternalMemory {
static const String AMNESTY_SPENT = "AMNESTY_SPENT_Jr4xVh9KmT3gLc6B";
static const String WATER = "WATER_Qe7tNd4ZsM2kBv9C";
static const String TODOS = "TODOS_Wm3kRb8TqZ5nHc2F";
static const String SHOW_ONBOARDING = "SHOW_ONBOARDING_Nz6qGf2SdX7wPb4M";
final groundedStorage = const FlutterSecureStorage();
@@ -252,6 +257,51 @@ class InternalMemory implements ConnectInternalMemory {
await groundedStorage.write(key: AMNESTY_SPENT, value: spent.toString());
}
String _today() {
final DateTime now = DateTime.now();
return "${now.year}-${now.month}-${now.day}";
}
@override
Future<int> getWaterToday() async {
String? value = await groundedStorage.read(key: WATER);
if (value != null) {
final Map<String, dynamic> entry = jsonDecode(value);
if (entry['day'] == _today()) {
return entry['ml'] ?? 0;
}
}
return 0;
}
@override
Future setWaterToday(int ml) async {
await groundedStorage.write(
key: WATER, value: jsonEncode({'day': _today(), 'ml': ml}));
}
@override
Future<List<TodoItem>> getTodos() async {
String? value = await groundedStorage.read(key: TODOS);
if (value != null) {
return (jsonDecode(value) as List<dynamic>)
.map((item) => TodoItem.fromJson(item as Map<String, dynamic>))
.toList();
}
return <TodoItem>[];
}
@override
Future setTodos(List<TodoItem> todos) async {
await groundedStorage.write(
key: TODOS,
value: jsonEncode(todos.map((item) => item.toJson()).toList()));
}
@override
Future<bool> showOnboarding() async {
String? value = await groundedStorage.read(key: SHOW_ONBOARDING);

View File

@@ -3,6 +3,7 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../about/internal/application/MeDescription.dart';
import '../about/internal/application/TodoItem.dart';
import '../about/internal/application/UserDetails.dart';
import '../memory/InternalMemory.dart';
import 'MockStorage.dart';
@@ -18,6 +19,12 @@ class MockMemory extends InternalMemory {
setMyDescription(
MeDescription(id: "mock-user", name: "Alex", token: "mock-token"));
setUserDetails(UserDetails(pic: "", name: "Alex"));
setWaterToday(1250);
setTodos(<TodoItem>[
TodoItem(id: "t1", title: "Refill water bottle"),
TodoItem(id: "t2", title: "Book physio for Thursday"),
TodoItem(id: "t3", title: "Reply to Sam about Saturday", done: true),
]);
}
}

View File

@@ -443,8 +443,8 @@ class NewCommitmentState extends State<NewCommitment>
decoration: BoxDecoration(
color: colorSheetBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
child: SingleChildScrollView(
@@ -460,7 +460,7 @@ class NewCommitmentState extends State<NewCommitment>
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: colorGrey.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
),
),
),

View File

@@ -142,7 +142,7 @@ class ExcuseReportState extends State<ExcuseReport>
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: colorInset,
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
border: Border.all(color: colorBorder, width: 1),
),
child: Row(

View File

@@ -242,7 +242,7 @@ class GoalDetailState extends State<GoalDetail>
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
decoration: BoxDecoration(
color: colorWhite.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.zero,
),
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -405,7 +405,7 @@ class GoalDetailState extends State<GoalDetail>
width: 58,
height: 58,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.zero,
child: SceneArt(
palette: scenePalette(routine.kind),
seed: sceneSeed(routine.title),
@@ -454,7 +454,6 @@ class GoalDetailState extends State<GoalDetail>
color: routine.kind == RoutineKind.MustDo
? colorPrimaryDark
: tone,
shape: BoxShape.circle,
),
child: Icon(CupertinoIcons.play_fill,
size: 15, color: colorWhite),

View File

@@ -282,7 +282,7 @@ class GoalEditorState extends State<GoalEditor> implements ConnectGoalEditor {
return Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.zero,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
@@ -338,7 +338,6 @@ class GoalEditorState extends State<GoalEditor> implements ConnectGoalEditor {
alignment: Alignment.center,
decoration: BoxDecoration(
color: colour,
shape: BoxShape.circle,
border: Border.all(
color: selected ? colorWhite : Colors.transparent,
width: 3,

View File

@@ -117,10 +117,10 @@ class GoalsState extends State<Goals> implements ConnectGoals {
return GestureDetector(
onTap: () => _onOpenGoal(goal),
child: Container(
height: 214,
height: 260,
margin: const EdgeInsets.only(bottom: 14),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(26),
borderRadius: BorderRadius.zero,
boxShadow: const [
BoxShadow(
color: Color(0x262A3A30),
@@ -130,7 +130,7 @@ class GoalsState extends State<Goals> implements ConnectGoals {
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(26),
borderRadius: BorderRadius.zero,
child: Stack(
fit: StackFit.expand,
children: [
@@ -151,7 +151,7 @@ class GoalsState extends State<Goals> implements ConnectGoals {
children: [
GlassSurface(
onDark: true,
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
child: text(

View File

@@ -1,7 +1,9 @@
import '../../about/external/data/Commitment.dart';
import '../../about/external/data/ExcuseCluster.dart';
import '../../about/external/data/ReportCard.dart';
import '../../about/external/data/RoutineChain.dart';
import '../../about/internal/application/Standing.dart';
import '../../about/internal/application/TodoItem.dart';
import '../../about/internal/application/UserDetails.dart';
abstract class ConnectHome {
@@ -14,6 +16,16 @@ abstract class ConnectHome {
/// The routine library, for the up-next rail.
void onRoutinesLoaded(List<RoutineChain> routines);
/// This week's report card, for the streak row, the score and the weekly
/// view. Null when it could not be loaded.
void onWeekLoaded(ReportCard? week);
/// Water drunk today, in ml.
void onWaterLoaded(int ml);
/// The quick to-do list, read from the device.
void onTodosLoaded(List<TodoItem> todos);
/// Standing arrives derived, with the debt it was derived from.
void onStandingResolved(Standing standing, double debtScore);

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
import '../../about/external/data/Commitment.dart';
import '../../about/external/data/ExcuseCluster.dart';
import '../../about/external/data/ReportCard.dart';
import '../../about/external/data/RoutineChain.dart';
import '../../about/external/data/pages/request/CommitmentsRequest.dart';
import '../../about/external/data/pages/request/HistoryRequest.dart';
@@ -11,6 +12,7 @@ import '../../about/external/initial/CompletionRequest.dart';
import '../../about/external/initial/ReportCardRequest.dart';
import '../../about/internal/application/CommitmentClass.dart';
import '../../about/internal/application/Standing.dart';
import '../../about/internal/application/TodoItem.dart';
import '../../about/internal/application/UserDetails.dart';
import '../../utils/DebtEngine.dart';
import '../../utils/GuardrailEngine.dart';
@@ -31,9 +33,51 @@ class ViewHome extends ParentViewModel {
final UserDetails cached = await getDataManager().getUserDetails();
connection.onUserLoaded(cached);
loadTodos();
loadWater();
loadWeek();
loadPlan();
}
/// The last seven days. Quiet on failure: the header falls back to today.
void loadWeek() async {
try {
final DateTime now = DateTime.now();
final DateTime start = DateTime(now.year, now.month, now.day)
.subtract(const Duration(days: 6));
final response =
await getDataManager().getWeeklyReportCard(ReportCardRequest(
periodStart: start.toIso8601String(),
periodEnd: now.toIso8601String(),
));
connection.onWeekLoaded(ReportCard.fromJson(response.data));
} catch (e) {
connection.onWeekLoaded(null);
}
}
void loadWater() async {
connection.onWaterLoaded(await getDataManager().getWaterToday());
}
void saveWater(int ml) async {
await getDataManager().setWaterToday(ml);
}
void loadTodos() async {
connection.onTodosLoaded(await getDataManager().getTodos());
}
/// Saves the whole list; it is small and only ever lives on the device.
void saveTodos(List<TodoItem> todos) async {
await getDataManager().setTodos(todos);
}
void loadPlan() async {
if (!await hasNetwork(() => loadPlan())) return;

View File

@@ -184,8 +184,8 @@ class LiveTaskState extends State<LiveTask>
decoration: BoxDecoration(
color: colorSheetBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
child: SingleChildScrollView(

View File

@@ -107,8 +107,8 @@ class LoginState extends State<Login> implements ConnectLogin {
decoration: BoxDecoration(
color: colorPrimaryLight,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
clipBehavior: Clip.antiAlias,

View File

@@ -147,8 +147,8 @@ class OverdueQueueState extends State<OverdueQueue>
decoration: BoxDecoration(
color: colorSheetBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
child: SingleChildScrollView(
@@ -166,7 +166,7 @@ class OverdueQueueState extends State<OverdueQueue>
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: colorGrey.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
),
),
),
@@ -242,8 +242,8 @@ class OverdueQueueState extends State<OverdueQueue>
decoration: BoxDecoration(
color: colorSheetBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
child: SingleChildScrollView(
@@ -261,7 +261,7 @@ class OverdueQueueState extends State<OverdueQueue>
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: colorGrey.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
),
),
),
@@ -343,8 +343,8 @@ class OverdueQueueState extends State<OverdueQueue>
decoration: BoxDecoration(
color: colorSheetBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
child: SingleChildScrollView(
@@ -360,7 +360,7 @@ class OverdueQueueState extends State<OverdueQueue>
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: colorGrey.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
),
),
),
@@ -504,7 +504,7 @@ class OverdueQueueState extends State<OverdueQueue>
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
decoration: BoxDecoration(
color: colorWhite.withValues(alpha: 0.10),
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.zero,
),
child: Row(
children: [

View File

@@ -152,7 +152,7 @@ class ParentViewModel extends ChangeNotifier {
height: 64,
decoration: BoxDecoration(
color: colorPrimaryDark,
borderRadius: BorderRadius.circular(18),
borderRadius: BorderRadius.zero,
),
child: Icon(CupertinoIcons.wifi_slash,
size: 26, color: colorWhite),
@@ -396,7 +396,7 @@ class ParentViewModel extends ChangeNotifier {
alignment: Alignment.center,
decoration: BoxDecoration(
color: colorStandingGroundedBg,
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
),
child: Icon(
CupertinoIcons.exclamationmark_triangle_fill,
@@ -544,8 +544,8 @@ class ParentViewModel extends ChangeNotifier {
decoration: BoxDecoration(
color: colorSheetBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
child: Column(
@@ -559,7 +559,7 @@ class ParentViewModel extends ChangeNotifier {
margin: const EdgeInsets.only(top: 12, bottom: 8),
decoration: BoxDecoration(
color: colorGrey.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
),
),
),
@@ -579,7 +579,7 @@ class ParentViewModel extends ChangeNotifier {
alignment: Alignment.center,
decoration: BoxDecoration(
color: actionBgColor,
borderRadius: BorderRadius.circular(15),
borderRadius: BorderRadius.zero,
),
child: Icon(actionIconData,
size: 24, color: actionColor),

View File

@@ -99,7 +99,7 @@ class ReportCardScreenState extends State<ReportCardScreen>
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
decoration: BoxDecoration(
color: colorWhite.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.zero,
),
child: Row(
children: [
@@ -128,7 +128,7 @@ class ReportCardScreenState extends State<ReportCardScreen>
alignment: Alignment.center,
decoration: BoxDecoration(
color: colorWhite,
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.zero,
),
child: text(_report.grade, 26, TextType.Light,
color: colorPrimaryDark),

View File

@@ -402,7 +402,7 @@ class RoutineBuilderState extends State<RoutineBuilder>
height: 124,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.zero,
gradient: selected
? LinearGradient(
begin: Alignment.topLeft,
@@ -424,7 +424,6 @@ class RoutineBuilderState extends State<RoutineBuilder>
color: selected
? colorWhite.withValues(alpha: 0.16)
: kindBackground(kind),
shape: BoxShape.circle,
),
child: Icon(kindIcon(kind),
size: 17, color: selected ? colorWhite : tone),
@@ -470,7 +469,7 @@ class RoutineBuilderState extends State<RoutineBuilder>
padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
decoration: BoxDecoration(
color: applied ? kindBackground(_routine.kind) : colorWhite,
borderRadius: BorderRadius.circular(14),
borderRadius: BorderRadius.zero,
border: Border.all(
color: applied
? kindColor(_routine.kind).withValues(alpha: 0.4)
@@ -515,7 +514,6 @@ class RoutineBuilderState extends State<RoutineBuilder>
alignment: Alignment.center,
decoration: BoxDecoration(
color: on ? tone : colorWhite,
shape: BoxShape.circle,
border: Border.all(color: on ? tone : colorBorder),
),
child: text(weekdayInitial(day), 13,
@@ -576,7 +574,7 @@ class RoutineBuilderState extends State<RoutineBuilder>
alignment: Alignment.center,
decoration: BoxDecoration(
color: kindBackground(_routine.kind),
borderRadius: BorderRadius.circular(9),
borderRadius: BorderRadius.zero,
),
child: text("${index + 1}", 12, TextType.Bold, color: tone),
),

View File

@@ -457,7 +457,7 @@ class RoutineRunnerState extends State<RoutineRunner>
Widget _totalPill() {
return GlassSurface(
onDark: true,
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 11),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -496,7 +496,7 @@ class RoutineRunnerState extends State<RoutineRunner>
track: colorWhite.withValues(alpha: 0.22),
child: GlassSurface(
onDark: true,
radius: BorderRadius.circular(100),
radius: BorderRadius.zero,
child: SizedBox(
width: 150,
height: 150,
@@ -552,7 +552,7 @@ class RoutineRunnerState extends State<RoutineRunner>
),
GlassSurface(
onDark: true,
radius: BorderRadius.circular(200),
radius: BorderRadius.zero,
child: SizedBox(
width: 120 + 50 * t,
height: 120 + 50 * t,
@@ -588,7 +588,7 @@ class RoutineRunnerState extends State<RoutineRunner>
children: [
GlassSurface(
onDark: true,
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -610,7 +610,7 @@ class RoutineRunnerState extends State<RoutineRunner>
const SizedBox(width: 8),
GlassSurface(
onDark: true,
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
child: text("Left app ${_run.backgroundedCount}×", 11,
TextType.Medium,
@@ -727,7 +727,6 @@ class RoutineRunnerState extends State<RoutineRunner>
height: size,
padding: const EdgeInsets.all(2.5),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isCurrent ? colorNight : colorWhite,
boxShadow: const [
BoxShadow(
@@ -737,7 +736,7 @@ class RoutineRunnerState extends State<RoutineRunner>
),
],
),
child: ClipOval(
child: ClipRect(
child: Stack(
fit: StackFit.expand,
children: [
@@ -794,7 +793,7 @@ class RoutineRunnerState extends State<RoutineRunner>
margin: const EdgeInsets.only(right: 4),
decoration: BoxDecoration(
color: filled || active ? tone : tone.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
),
);
}),
@@ -894,7 +893,7 @@ class RoutineRunnerState extends State<RoutineRunner>
alignment: Alignment.center,
decoration: BoxDecoration(
color: colorNight,
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
boxShadow: const [
BoxShadow(
color: Color(0x331A1F1C),
@@ -956,8 +955,8 @@ class RoutineRunnerState extends State<RoutineRunner>
decoration: const BoxDecoration(
color: colorSheetBackground,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
child: ListView(
@@ -985,7 +984,7 @@ class RoutineRunnerState extends State<RoutineRunner>
padding: const EdgeInsets.fromLTRB(12, 12, 14, 12),
decoration: BoxDecoration(
color: current ? kindBackground(_run.chain.kind) : colorWhite,
borderRadius: BorderRadius.circular(14),
borderRadius: BorderRadius.zero,
border: Border.all(
color: current ? tone.withValues(alpha: 0.35) : colorBorder,
),
@@ -998,7 +997,6 @@ class RoutineRunnerState extends State<RoutineRunner>
alignment: Alignment.center,
decoration: BoxDecoration(
color: step.completed ? tone : colorSecondary,
shape: BoxShape.circle,
),
child: step.completed
? Icon(Icons.check_rounded,
@@ -1072,7 +1070,7 @@ class RoutineRunnerState extends State<RoutineRunner>
children: [
GlassSurface(
onDark: true,
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 7),
child: text(
@@ -1096,7 +1094,7 @@ class RoutineRunnerState extends State<RoutineRunner>
track: colorWhite.withValues(alpha: 0.3),
child: GlassSurface(
onDark: true,
radius: BorderRadius.circular(100),
radius: BorderRadius.zero,
child: SizedBox(
width: 158,
height: 158,
@@ -1128,7 +1126,7 @@ class RoutineRunnerState extends State<RoutineRunner>
),
const SizedBox(height: 22),
GlassSurface(
radius: BorderRadius.circular(24),
radius: BorderRadius.zero,
tint: colorGlassStrong,
padding: const EdgeInsets.fromLTRB(18, 16, 18, 16),
child: Row(

View File

@@ -330,7 +330,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
..sort((a, b) => a.startMinute.compareTo(b.startMinute));
return GlassSurface(
radius: BorderRadius.circular(24),
radius: BorderRadius.zero,
tint: colorGlassStrong,
padding: const EdgeInsets.fromLTRB(18, 16, 18, 16),
child: Row(
@@ -391,18 +391,17 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
onTap: () => _onSelectKind(kind),
behavior: HitTestBehavior.opaque,
child: SizedBox(
width: 74,
width: 88,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut,
width: 64,
height: 64,
width: 78,
height: 78,
padding: const EdgeInsets.all(2.5),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: selected ? colorNight : colorWhite,
boxShadow: const [
BoxShadow(
@@ -412,7 +411,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
),
],
),
child: ClipOval(
child: ClipRect(
child: kind == null
? Container(
color: colorWhite,
@@ -450,7 +449,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
final List<RoutineChain> items = _upNext;
return SizedBox(
height: 240,
height: 290,
child: ListView.separated(
scrollDirection: Axis.horizontal,
clipBehavior: Clip.none,
@@ -470,9 +469,9 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
return GestureDetector(
onTap: () => _onPreview(routine),
child: Container(
width: 200,
width: 244,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(26),
borderRadius: BorderRadius.zero,
boxShadow: const [
BoxShadow(
color: Color(0x262A3A30),
@@ -482,7 +481,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(26),
borderRadius: BorderRadius.zero,
child: Stack(
fit: StackFit.expand,
children: [
@@ -503,7 +502,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
children: [
GlassSurface(
onDark: true,
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
child: text(
@@ -611,10 +610,10 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
child: Row(
children: [
SizedBox(
width: 70,
height: 70,
width: 90,
height: 90,
child: ClipRRect(
borderRadius: BorderRadius.circular(18),
borderRadius: BorderRadius.zero,
child: SceneArt(
palette: scenePalette(routine.kind),
seed: sceneSeed(routine.title),
@@ -665,7 +664,6 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
alignment: Alignment.center,
decoration: BoxDecoration(
color: tone,
shape: BoxShape.circle,
),
child: Icon(Icons.check_rounded,
size: 22, color: colorWhite),
@@ -717,8 +715,8 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
decoration: const BoxDecoration(
color: colorAmbientMid,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
clipBehavior: Clip.antiAlias,
@@ -755,7 +753,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
height: 4,
decoration: BoxDecoration(
color: colorWhite.withValues(alpha: 0.7),
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
),
),
),
@@ -772,7 +770,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
children: [
GlassSurface(
onDark: true,
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
child: text(kindLabel(routine.kind), 10,
@@ -782,7 +780,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
const SizedBox(width: 6),
GlassSurface(
onDark: true,
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
child: text(
@@ -816,7 +814,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
const SizedBox(height: 16),
],
GlassSurface(
radius: BorderRadius.circular(22),
radius: BorderRadius.zero,
tint: colorGlassStrong,
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
child: Row(
@@ -895,7 +893,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
padding: const EdgeInsets.fromLTRB(10, 10, 14, 10),
decoration: BoxDecoration(
color: colorWhite,
borderRadius: BorderRadius.circular(18),
borderRadius: BorderRadius.zero,
),
child: Row(
children: [
@@ -905,7 +903,6 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
alignment: Alignment.center,
decoration: BoxDecoration(
color: tone.withValues(alpha: 0.10),
shape: BoxShape.circle,
),
child: text("${index + 1}", 12, TextType.Medium, color: tone),
),

View File

@@ -67,8 +67,8 @@ class StepEditorState extends State<StepEditor> {
decoration: const BoxDecoration(
color: colorSheetBackground,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
child: SingleChildScrollView(
@@ -234,7 +234,7 @@ class StepEditorState extends State<StepEditor> {
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
decoration: BoxDecoration(
color: kindBackground(widget.kind),
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.zero,
),
child: Row(
children: [

View File

@@ -93,8 +93,8 @@ class SettingsState extends State<Settings> implements ConnectSettings {
decoration: BoxDecoration(
color: colorSheetBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
topLeft: Radius.zero,
topRight: Radius.zero,
),
),
child: SingleChildScrollView(
@@ -112,7 +112,7 @@ class SettingsState extends State<Settings> implements ConnectSettings {
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: colorGrey.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(999),
borderRadius: BorderRadius.zero,
),
),
),
@@ -222,7 +222,7 @@ class SettingsState extends State<Settings> implements ConnectSettings {
color: _amnestyRemaining > 0
? colorStandingGoodBg
: colorMuted,
borderRadius: BorderRadius.circular(14),
borderRadius: BorderRadius.zero,
),
child: Icon(
CupertinoIcons.checkmark_shield_fill,

View File

@@ -239,7 +239,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
],
),
GlassSurface(
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -299,11 +299,11 @@ class SplashState extends State<Splash> implements ConnectSplash {
Widget _mark() {
return GlassSurface(
radius: BorderRadius.circular(46),
radius: BorderRadius.zero,
padding: const EdgeInsets.all(10),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(34),
borderRadius: BorderRadius.zero,
boxShadow: <BoxShadow>[
BoxShadow(
color: colorPrimary.withValues(alpha: 0.4),
@@ -342,7 +342,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
alignment: alignment,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(99),
borderRadius: BorderRadius.zero,
boxShadow: const [
BoxShadow(
color: Color(0x142A3A30),
@@ -352,7 +352,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
],
),
child: GlassSurface(
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
tint: colorGlassStrong,
padding: const EdgeInsets.fromLTRB(4, 4, 12, 4),
child: Row(
@@ -424,7 +424,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
/// The whole idea in three beats, on glass over the valley.
Widget _howItWorks() {
return GlassSurface(
radius: BorderRadius.circular(24),
radius: BorderRadius.zero,
tint: colorGlassStrong,
padding: const EdgeInsets.fromLTRB(14, 14, 14, 14),
child: Row(
@@ -499,7 +499,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
return Column(
children: [
GlassSurface(
radius: BorderRadius.circular(99),
radius: BorderRadius.zero,
padding: const EdgeInsets.all(3),
child: SizedBox(
width: 150,
@@ -510,7 +510,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
width: 150,
decoration: BoxDecoration(
color: colorNight,
borderRadius: BorderRadius.circular(99),
borderRadius: BorderRadius.zero,
),
).animate().scaleX(
begin: 0.04,

View File

@@ -71,7 +71,7 @@ class SessionExpiredState extends State<SessionExpired>
alignment: Alignment.center,
decoration: BoxDecoration(
color: colorWhite.withValues(alpha: 0.10),
borderRadius: BorderRadius.circular(18),
borderRadius: BorderRadius.zero,
),
child: Icon(CupertinoIcons.lock_fill,
size: 24, color: colorWhite),

View File

@@ -68,7 +68,7 @@ class UpdateMeState extends State<UpdateMe> implements ConnectUpdateMe {
alignment: Alignment.center,
decoration: BoxDecoration(
color: colorWhite.withValues(alpha: 0.10),
borderRadius: BorderRadius.circular(18),
borderRadius: BorderRadius.zero,
),
child: Icon(CupertinoIcons.arrow_up_circle_fill,
size: 24, color: colorWhite),

View File

@@ -127,6 +127,21 @@ const Color colorOverdueWarm = Color(0xFFF9E1D8);
const Color colorOverdueWarmEnd = Color(0xFFFFF6EF);
const Color colorOverdueGlow = Color(0xFFD7654B);
// ── Home header ───────────────────────────────────────────────────────────────
// The deep forest block the day sits in, with brighter bars that read on it.
const Color colorHeaderTop = Color(0xFF203D30);
const Color colorHeaderBottom = Color(0xFF15281F);
const Color colorHeaderPanel = Color(0x14FFFFFF);
const Color colorHeaderLine = Color(0x26FFFFFF);
const Color colorHeaderMuted = Color(0x99FFFFFF);
const Color colorHeaderSelected = Color(0xFF3D6B53);
const Color colorBarNonNegotiable = Color(0xFFE8735A);
const Color colorBarStandard = Color(0xFFF2C14E);
const Color colorBarElective = Color(0xFF6FD39B);
// Water: filled drops and the empty ones.
const Color colorWater = Color(0xFF6E8EF0);
const Color colorWaterEmpty = Color(0xFFDDE1DE);
// ── Scene art ─────────────────────────────────────────────────────────────────
// The painted landscapes that stand in for photography on cards. Each kind has
// a sky (top → horizon), a sun, three ridges (far → near) and an ember accent.

View File

@@ -48,7 +48,7 @@ class GroundedApp extends StatelessWidget {
fontSize: 17,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.zero,
),
),
),