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:
@@ -111,7 +111,8 @@ status field. That's what makes honest history and excuse analysis possible.
|
|||||||
|
|
||||||
See `design.md`. In short: sage-to-cream ambient backdrop, frosted glass
|
See `design.md`. In short: sage-to-cream ambient backdrop, frosted glass
|
||||||
controls, painted `SceneArt` instead of photos, image-led cards, two-tone
|
controls, painted `SceneArt` instead of photos, image-led cards, two-tone
|
||||||
greetings, square night buttons with a lime spark, no rounded corners.
|
greetings, night buttons with a lime spark, softly rounded corners (never
|
||||||
|
pill-soft) and a floating round-button nav that switches tabs in place.
|
||||||
|
|
||||||
- Screens are built from `Sheet` (`designs/Shell.dart`); Home, Routines and
|
- Screens are built from `Sheet` (`designs/Shell.dart`); Home, Routines and
|
||||||
the routine player compose `AmbientBackground` + glass directly.
|
the routine player compose `AmbientBackground` + glass directly.
|
||||||
@@ -121,7 +122,7 @@ greetings, square night buttons with a lime spark, no rounded corners.
|
|||||||
- Standing owns a colour used consistently everywhere (chrome, chip, meter).
|
- Standing owns a colour used consistently everywhere (chrome, chip, meter).
|
||||||
- One primary button per screen; everything else outlined or text.
|
- One primary button per screen; everything else outlined or text.
|
||||||
|
|
||||||
Font: **General Sans** (Fontshare, FFL — see `fonts/LICENSE-GeneralSans.txt`),
|
Font: **Inter** (SIL OFL — see `fonts/LICENSE-Inter.txt`; Light is Inter Display),
|
||||||
wrapped as `TextType.{Bold,Medium,Regular,Light}`.
|
wrapped as `TextType.{Bold,Medium,Regular,Light}`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ build their own layouts from the same pieces.
|
|||||||
The sections below describe the original record-style type scale, which still
|
The sections below describe the original record-style type scale, which still
|
||||||
applies to labels and metrics.
|
applies to labels and metrics.
|
||||||
|
|
||||||
## Type — General Sans
|
## Type — Inter
|
||||||
|
|
||||||
|
`TextType.Light` is Inter Display Regular (the display cut, for titles and
|
||||||
|
big numbers); Regular, Medium and Bold are Inter Regular, Medium and
|
||||||
|
SemiBold.
|
||||||
|
|
||||||
| Role | Family | Size | Use |
|
| Role | Family | Size | Use |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
@@ -45,7 +49,8 @@ directly above a large dark value. Used everywhere numbers appear.
|
|||||||
|
|
||||||
## Geometry
|
## Geometry
|
||||||
|
|
||||||
- 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.
|
- Softly rounded, never pill-soft: large cards and image cards `16`, buttons `14`, rows, tiles and fields `12`, small controls `10`, chips and badges `8`, bars `4`. Sheets round their top at `20`.
|
||||||
|
- Portraits, the nav buttons, status dots, rings and the breathing circle are full circles.
|
||||||
- Screen padding `20` horizontal. Card padding `16`. Section gap `28`.
|
- Screen padding `20` horizontal. Card padding `16`. Section gap `28`.
|
||||||
- Hairlines at `colorDivider` (black 7%), borders at `colorBorder` (black 8%).
|
- 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.
|
- One soft shadow only: `0 4 18 rgba(0,0,0,0.03)`. No coloured glows.
|
||||||
@@ -66,6 +71,14 @@ Near-black ink (`colorPrimaryDark #141414`) on warm off-white
|
|||||||
The tier colour is used consistently — header, chip, meter, queue accent — so
|
The tier colour is used consistently — header, chip, meter, queue accent — so
|
||||||
the standing is legible without reading the word.
|
the standing is legible without reading the word.
|
||||||
|
|
||||||
|
## Navigation
|
||||||
|
|
||||||
|
The nav is a floating cluster of round frosted buttons, icons only; the
|
||||||
|
open tab is a raised white disc. It switches tabs inside Home (Today,
|
||||||
|
Routines, Goals, Groups, Progress) in place: nothing is pushed, a tab has
|
||||||
|
no back arrow (`tab: true`), and Android back returns to Today. Tabs are
|
||||||
|
built on first open and then kept.
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
Every screen is: black `headerBar` (back, title, action) → white sheet with a
|
Every screen is: black `headerBar` (back, title, action) → white sheet with a
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
Fontshare EULA
|
|
||||||
|
|
||||||
---—---------------------------------—------------------------------
|
|
||||||
Free Font - End User License Agreement (FF EULA)
|
|
||||||
---—---------------------------------—------------------------------
|
|
||||||
Notice to User
|
|
||||||
Indian Type Foundry designs, produces and distributes font software as digital fonts to end users worldwide. In addition to commercial fonts that are available for a fee, ITF also offers several fonts which can be used free of charge. The free fonts are distributed through a dedicated platform called www.fontshare.com (“Fontshare”) to end users worldwide. These free fonts are subject to this legally binding EULA between the Indian Type Foundry (“Indian Type Foundry” or “Licensor”) and you (“Licensee”).
|
|
||||||
You acknowledge that the Font Software and designs embodied therein are protected by the copyright, other intellectual property rights and industrial property rights and by international treaties. They are and remain at all times the intellectual property of the Indian Type Foundry.
|
|
||||||
In addition to direct download, Fontshare also offers these free fonts via Fonthsare API using a code. In this case, the Font Software is delivered directly from the servers used by Indian Type Foundry to the Licensee's website, without the Licensee having to download the Font Software.
|
|
||||||
By downloading, accessing the API, installing, storing, copying or using one of any Font Software, you agree to the following terms.
|
|
||||||
|
|
||||||
Definitions
|
|
||||||
“Font Software” refers to the set of computer files or programs released under this license that instructs your computer to display and/or print each letters, characters, typographic designs, ornament and so forth. Font Software includes all bitmap and vector representations of fonts and typographic representations and embellishments created by or derived from the Font Software.
|
|
||||||
“Original Version” refers to the Font Software as distributed by the Indian Type Foundry as the copyright holder.
|
|
||||||
“Derivative Work” refers to the pictorial representation of the font created by the Font Software, including typographic characters such as letters, numerals, ornaments, symbols, or punctuation and special characters.
|
|
||||||
|
|
||||||
01. Grant of License
|
|
||||||
You are hereby granted a non-exclusive, non-assignable, non-transferrable, terminable license to access, download and use the Font Software for your personal or commercial use for an unlimited period of time for free of charge.
|
|
||||||
You may use the font Software in any media (including Print, Web, Mobile, Digital, Apps, ePub, Broadcasting and OEM) at any scale, at any location worldwide.
|
|
||||||
You may use the Font Software to create logos and other graphic elements, images on any surface, vector files or other scalable drawings and static images.
|
|
||||||
You may use the Font Software on any number of devices (computer, tablet, phone). The number of output devices (Printers) is not restricted.
|
|
||||||
You may make only such reasonable number of back-up copies suitable to your permitted use.
|
|
||||||
You may but are not required to identify Indian Type Foundry Fonts in your work credits.
|
|
||||||
|
|
||||||
02. Limitations of usage
|
|
||||||
You may not modify, edit, adapt, translate, reverse engineer, decompile or disassemble, alter or otherwise copy the Font Software or the designs embodied therein in whole or in part, without the prior written consent of the Licensor.
|
|
||||||
The Fonts may not - beyond the permitted copies and the uses defined herein - be distributed, duplicated, loaned, resold or licensed in any way, whether by lending, donating or give otherwise to a person or entity. This includes the distribution of the Fonts by e-mail, on USB sticks, CD-ROMs, or other media, uploading them in a public server or making the fonts available on peer-to-peer networks. A passing on to external designers or service providers (design agencies, repro studios, printers, etc.) is also not permitted.
|
|
||||||
You are not allowed to transmit the Font Software over the Internet in font serving or for font replacement by means of technologies such as but not limited to EOT, Cufon, sIFR or similar technologies that may be developed in the future without the prior written consent of the Licensor.
|
|
||||||
|
|
||||||
03. Embedding
|
|
||||||
You may embed the Font Software in PDF and other digital documents provided that is done in a secured, read-only mode. It must be ensured beyond doubt that the recipient cannot use the Font Software to edit or to create new documents. The design data (PDFs) created in this way and under these created design data (PDFs) may be distributed in any number.
|
|
||||||
The extraction of the Font Software in whole or in part is prohibited.
|
|
||||||
|
|
||||||
04. Third party use, Commercial print service provider
|
|
||||||
You may include the Font Software in a non-editable electronic document solely for printing and display purposes and provide that electronic document to the commercial print service provider for the purpose of printing. If the print service needs to install the fonts, they too need to download the Font Software from the Licensor's website.
|
|
||||||
|
|
||||||
05. Derivative Work
|
|
||||||
You are allowed to make derivative works as far as you use them for your personal or commercial use. However, you cannot modify, make changes or reverse engineer the original font software provided to you. Any derivative works are the exclusive property of the Licensor and shall be subject to the terms and conditions of this EULA. Derivative works may not be sub-licensed, sold, leased, rented, loaned, or given away without the express written permission of the Licensor.
|
|
||||||
|
|
||||||
06. Warranty and Liability
|
|
||||||
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, INDIAN TYPE FOUNDRY MAKES NO WARRANTIES, EXPRESS OR IMPLIED AS TO THE MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR OTHERWISE. THE FONT SOFTWARE WAS NOT MANUFACTURED FOR USE IN MANUFACTURING CONTROL DEVICES OR NAVIGATION DEVICES OR IN CIRCUMSTANCES THAT COULD RESULT IN ENVIRONMENTAL DAMAGE OR PERSONAL INJURY. WITHOUT LIMITING THE FOREGOING, INDIAN TYPE FOUNDRY SHALL IN NO EVENT BE LIABLE TO THE LICENSED USER OR ANY OTHER THIRD PARTY FOR ANY DIRECT, CONSEQUENTIAL OR INCIDENTAL DAMAGES, INCLUDING DAMAGES FROM LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION NOR FOR LOST PROFITS OR SAVINGS ARISING OUT OF THE USE OR INABILITY TO USE THE PRODUCT EVEN IF NOTIFIED IN ADVANCE, UNDER NO CIRCUMSTANCES SHALL INDIAN TYPE FOUNDRY’S LIABILITY EXCEED THE REPLACEMENT COST OF THE SOFTWARE.
|
|
||||||
IF LICENSEE CHOOSES TO ACCESS THE FONT SOFTWARE THROUGH A CODE (API), IT MAY HAVE A DIRECT IMPACT ON LICENSEE'S WEBSITE OR APPLICATIONS. INDIAN TYPE FOUNDRY IS NOT RESPONSIBLE OR LIABLE FOR ANY INTERRUPTION, MALFUNCTION, DOWNTIME OR OTHER FAILURE OF THE WEBSITE OR ITS API.
|
|
||||||
|
|
||||||
07. Updates, Maintenance and Support Services
|
|
||||||
Licensor will not provide you with any support services for the Software under this Agreement.
|
|
||||||
|
|
||||||
08. Termination
|
|
||||||
Any breach of the terms of this agreement shall be a cause for termination, provided that such breach is notified in writing to the Licensee by the Licensor and the Licensee failed to rectify the breach within 30 days of the receipt of such notification.
|
|
||||||
In the event of termination and without limitation of any remedies under law or equity, you must delete the Font Software and all copies thereof. Proof of this must be provided upon request of the Licensor.
|
|
||||||
We reserve the right to claim damages for the violation of the conditions.
|
|
||||||
|
|
||||||
09. Final Provisions
|
|
||||||
If individual provisions of this agreement are or become invalid, the validity of the remaining provisions shall remain unaffected. Invalid provisions shall be replaced by mutual agreement by such provisions that are suitable to achieve the desired economic purpose, taking into account the interests of both parties. The same shall apply mutatis mutandis to the filling of any gaps which may arise in this agreement.
|
|
||||||
This contract is subject to laws of the Republic of India. Place of performance and exclusive place of jurisdiction for all disputes between the parties arising out of or in connection with this contract is, as far as legally permissible, Ahmedabad, India.
|
|
||||||
-
|
|
||||||
Last Updated on 22 March 2021
|
|
||||||
Copyright 2021 Indian Type Foundry. All rights reserved.
|
|
||||||
92
frontend/fonts/LICENSE-Inter.txt
Normal file
92
frontend/fonts/LICENSE-Inter.txt
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
Copyright (c) 2016 The Inter Project Authors (https://github.com/rsms/inter)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION AND CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -48,7 +48,7 @@ class TickBox extends StatelessWidget {
|
|||||||
height: size,
|
height: size,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: checked ? tone : Colors.transparent,
|
color: checked ? tone : Colors.transparent,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(6),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: checked ? tone : colorGrey.withValues(alpha: 0.55),
|
color: checked ? tone : colorGrey.withValues(alpha: 0.55),
|
||||||
width: 1.6,
|
width: 1.6,
|
||||||
@@ -202,7 +202,7 @@ Widget addTaskAffordance(
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: tone.withValues(alpha: 0.12),
|
color: tone.withValues(alpha: 0.12),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
border: Border.all(color: tone.withValues(alpha: 0.25), width: 1),
|
border: Border.all(color: tone.withValues(alpha: 0.25), width: 1),
|
||||||
),
|
),
|
||||||
child: Icon(Icons.add_rounded, size: 22, color: tone),
|
child: Icon(Icons.add_rounded, size: 22, color: tone),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import '../utils/Colors.dart';
|
|||||||
import 'text/Text.dart';
|
import 'text/Text.dart';
|
||||||
|
|
||||||
/// The font families, wrapped so no screen ever names a family directly.
|
/// The font families, wrapped so no screen ever names a family directly.
|
||||||
/// General Sans — see fonts/LICENSE-GeneralSans.txt.
|
/// Inter (SIL OFL) — see fonts/LICENSE-Inter.txt.
|
||||||
String getTextType(TextType type) {
|
String getTextType(TextType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TextType.Bold:
|
case TextType.Bold:
|
||||||
@@ -32,7 +32,7 @@ Widget pill(
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: background,
|
color: background,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
border: Border.all(color: foreground.withValues(alpha: 0.20), width: 1),
|
border: Border.all(color: foreground.withValues(alpha: 0.20), width: 1),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -61,7 +61,7 @@ Widget card({
|
|||||||
EdgeInsets margin = EdgeInsets.zero,
|
EdgeInsets margin = EdgeInsets.zero,
|
||||||
Color? background,
|
Color? background,
|
||||||
Color? borderColor,
|
Color? borderColor,
|
||||||
double radius = 22,
|
double radius = 16,
|
||||||
VoidCallback? onTap,
|
VoidCallback? onTap,
|
||||||
}) {
|
}) {
|
||||||
final Widget body = Container(
|
final Widget body = Container(
|
||||||
@@ -70,7 +70,7 @@ Widget card({
|
|||||||
margin: margin,
|
margin: margin,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: background ?? colorCard,
|
color: background ?? colorCard,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(radius),
|
||||||
border: Border.all(color: borderColor ?? colorGlassBorder, width: 1),
|
border: Border.all(color: borderColor ?? colorGlassBorder, width: 1),
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@@ -159,6 +159,7 @@ Widget emptyState(
|
|||||||
height: 76,
|
height: 76,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: tone.withValues(alpha: 0.08),
|
color: tone.withValues(alpha: 0.08),
|
||||||
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(icon, size: 32, color: tone),
|
child: Icon(icon, size: 32, color: tone),
|
||||||
),
|
),
|
||||||
@@ -190,7 +191,7 @@ Widget meter(
|
|||||||
: fraction;
|
: fraction;
|
||||||
|
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
value: clamped,
|
value: clamped,
|
||||||
minHeight: height,
|
minHeight: height,
|
||||||
|
|||||||
@@ -1,125 +1,100 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../about/internal/application/TextType.dart';
|
|
||||||
import '../utils/Colors.dart';
|
import '../utils/Colors.dart';
|
||||||
import 'GlassSurface.dart';
|
import 'GlassSurface.dart';
|
||||||
import 'text/Text.dart';
|
|
||||||
|
|
||||||
typedef NavEntry = ({IconData icon, String label, VoidCallback onTap});
|
typedef NavEntry = ({IconData icon, String label, VoidCallback onTap});
|
||||||
|
|
||||||
/// The floating tab bar: the selected tab is a filled night block, the rest
|
/// The floating tab bar: a cluster of round frosted buttons, icons only.
|
||||||
/// sit quietly on white. An optional dark action button can sit beside it.
|
/// The open tab rises out of the row as a solid white disc. It switches tabs
|
||||||
|
/// in place; nothing here pushes a page.
|
||||||
class FloatingNav extends StatelessWidget {
|
class FloatingNav extends StatelessWidget {
|
||||||
final List<NavEntry> entries;
|
final List<NavEntry> entries;
|
||||||
|
|
||||||
final int selected;
|
final int selected;
|
||||||
|
|
||||||
final IconData? actionIcon;
|
|
||||||
|
|
||||||
final VoidCallback? onAction;
|
|
||||||
|
|
||||||
const FloatingNav({
|
const FloatingNav({
|
||||||
super.key,
|
super.key,
|
||||||
required this.entries,
|
required this.entries,
|
||||||
required this.selected,
|
required this.selected,
|
||||||
this.actionIcon,
|
|
||||||
this.onAction,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static const double _size = 58;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 14),
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||||
child: Row(
|
child: Center(
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.all(Radius.circular(_size)),
|
||||||
boxShadow: const [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Color(0x1A1A1F1C),
|
color: Color(0x1F1A1F1C),
|
||||||
blurRadius: 30,
|
blurRadius: 30,
|
||||||
offset: Offset(0, 10),
|
offset: Offset(0, 12),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: GlassSurface(
|
child: GlassSurface(
|
||||||
radius: BorderRadius.zero,
|
radius: const BorderRadius.all(Radius.circular(_size)),
|
||||||
tint: colorGlassStrong,
|
tint: colorGlass,
|
||||||
padding: const EdgeInsets.all(6),
|
padding: const EdgeInsets.all(5),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
for (int index = 0; index < entries.length; index++)
|
for (int index = 0; index < entries.length; index++) ...[
|
||||||
Expanded(child: _tab(entries[index], index == selected)),
|
if (index > 0) const SizedBox(width: 4),
|
||||||
],
|
_button(entries[index], index == selected),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (onAction != null) ...[
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: onAction,
|
|
||||||
child: Container(
|
|
||||||
width: 72,
|
|
||||||
height: 72,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: const RadialGradient(
|
|
||||||
center: Alignment(-0.3, -0.4),
|
|
||||||
colors: <Color>[colorNightGlow, colorNight],
|
|
||||||
),
|
|
||||||
border: Border.all(color: colorWhite, width: 3),
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: Color(0x331A1F1C),
|
|
||||||
blurRadius: 24,
|
|
||||||
offset: Offset(0, 10),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Icon(actionIcon, size: 24, color: colorLime),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _tab(NavEntry entry, bool active) {
|
Widget _button(NavEntry entry, bool active) {
|
||||||
return GestureDetector(
|
return Semantics(
|
||||||
|
button: true,
|
||||||
|
selected: active,
|
||||||
|
label: entry.label,
|
||||||
|
child: Tooltip(
|
||||||
|
message: entry.label,
|
||||||
|
child: GestureDetector(
|
||||||
onTap: entry.onTap,
|
onTap: entry.onTap,
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 220),
|
duration: const Duration(milliseconds: 220),
|
||||||
curve: Curves.easeOutCubic,
|
curve: Curves.easeOutCubic,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
width: _size,
|
||||||
|
height: _size,
|
||||||
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: active ? colorHeaderBottom : colorWhite.withValues(alpha: 0.0),
|
shape: BoxShape.circle,
|
||||||
borderRadius: BorderRadius.zero,
|
color: active ? colorWhite : colorWhite.withValues(alpha: 0.35),
|
||||||
|
border: Border.all(
|
||||||
|
color: colorWhite.withValues(alpha: active ? 1 : 0.6),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
boxShadow: active
|
boxShadow: active
|
||||||
? const [
|
? const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Color(0x141A1F1C),
|
color: Color(0x261A1F1C),
|
||||||
blurRadius: 12,
|
blurRadius: 16,
|
||||||
offset: Offset(0, 4),
|
offset: Offset(0, 6),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: const [],
|
: const [],
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Icon(entry.icon,
|
||||||
mainAxisSize: MainAxisSize.min,
|
size: 22, color: active ? colorInk : colorInkSoft),
|
||||||
children: [
|
),
|
||||||
Icon(entry.icon,
|
|
||||||
size: 23, color: active ? colorWhite : colorInkSoft),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
text(entry.label, 10, active ? TextType.Medium : TextType.Regular,
|
|
||||||
color: active ? colorWhite : colorInkSoft, maxLines: 1),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class GlassSurface extends StatelessWidget {
|
|||||||
const GlassSurface({
|
const GlassSurface({
|
||||||
super.key,
|
super.key,
|
||||||
required this.child,
|
required this.child,
|
||||||
this.radius = const BorderRadius.all(Radius.zero),
|
this.radius = const BorderRadius.all(Radius.circular(14)),
|
||||||
this.padding = EdgeInsets.zero,
|
this.padding = EdgeInsets.zero,
|
||||||
this.tint,
|
this.tint,
|
||||||
this.onDark = false,
|
this.onDark = false,
|
||||||
@@ -73,7 +73,7 @@ Widget glassCircle(
|
|||||||
children: [
|
children: [
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
onDark: onDark,
|
onDark: onDark,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(size),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ Future<Goal?> showGoalPicker(
|
|||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: colorSheetBackground,
|
color: colorSheetBackground,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
@@ -93,7 +93,7 @@ Widget _goalOption(
|
|||||||
padding: const EdgeInsets.fromLTRB(14, 13, 14, 13),
|
padding: const EdgeInsets.fromLTRB(14, 13, 14, 13),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: selected ? accent.withValues(alpha: 0.5) : colorBorder,
|
color: selected ? accent.withValues(alpha: 0.5) : colorBorder,
|
||||||
width: selected ? 1.5 : 1,
|
width: selected ? 1.5 : 1,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import '../utils/CommonUtils.dart';
|
|||||||
import 'scene/SceneArt.dart';
|
import 'scene/SceneArt.dart';
|
||||||
import 'text/Text.dart';
|
import 'text/Text.dart';
|
||||||
|
|
||||||
/// A member's square portrait: a landscape picked from their name with the
|
/// A member's round portrait: a landscape picked from their name with the
|
||||||
/// initial on it, so everyone is recognisable without a photo.
|
/// initial on it, so everyone is recognisable without a photo.
|
||||||
Widget memberAvatar(String name, double size, {Color? border}) {
|
Widget memberAvatar(String name, double size, {Color? border}) {
|
||||||
const List<List<Color>> palettes = <List<Color>>[
|
const List<List<Color>> palettes = <List<Color>>[
|
||||||
@@ -21,17 +21,22 @@ Widget memberAvatar(String name, double size, {Color? border}) {
|
|||||||
return Container(
|
return Container(
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
decoration:
|
decoration: BoxDecoration(
|
||||||
BoxDecoration(border: Border.all(color: border ?? colorWhite, width: 2)),
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(color: border ?? colorWhite, width: 2),
|
||||||
|
),
|
||||||
|
child: ClipOval(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
SceneArt(palette: palettes[seed % palettes.length], seed: seed),
|
SceneArt(palette: palettes[seed % palettes.length], seed: seed),
|
||||||
Center(
|
Center(
|
||||||
child: text(initial, size * 0.42, TextType.Medium, color: colorWhite),
|
child:
|
||||||
|
text(initial, size * 0.42, TextType.Medium, color: colorWhite),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +66,7 @@ Widget avatarStack(List<String> names, double size,
|
|||||||
height: size,
|
height: size,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
color: colorNight,
|
color: colorNight,
|
||||||
border: Border.all(color: border ?? colorWhite, width: 2),
|
border: Border.all(color: border ?? colorWhite, width: 2),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ class Sheet extends StatelessWidget {
|
|||||||
/// Optional trailing control in the chrome.
|
/// Optional trailing control in the chrome.
|
||||||
final Widget? action;
|
final Widget? action;
|
||||||
|
|
||||||
|
/// Space under the content; tabs leave room for the floating nav.
|
||||||
|
final double bottomSpace;
|
||||||
|
|
||||||
/// Stage colour. Leaving it null (with no banner) gives the ambient tone.
|
/// Stage colour. Leaving it null (with no banner) gives the ambient tone.
|
||||||
final Color? chrome;
|
final Color? chrome;
|
||||||
|
|
||||||
@@ -53,6 +56,7 @@ class Sheet extends StatelessWidget {
|
|||||||
this.scrollable = true,
|
this.scrollable = true,
|
||||||
this.scene,
|
this.scene,
|
||||||
this.sceneSeed = 0,
|
this.sceneSeed = 0,
|
||||||
|
this.bottomSpace = 40,
|
||||||
});
|
});
|
||||||
|
|
||||||
bool get _light => chrome == null && banner == null && scene == null;
|
bool get _light => chrome == null && banner == null && scene == null;
|
||||||
@@ -62,8 +66,7 @@ class Sheet extends StatelessWidget {
|
|||||||
final bool light = _light;
|
final bool light = _light;
|
||||||
final Color stage = chrome ?? colorPrimaryDark;
|
final Color stage = chrome ?? colorPrimaryDark;
|
||||||
final Color ink = light ? colorInk : colorWhite;
|
final Color ink = light ? colorInk : colorWhite;
|
||||||
final Color soft =
|
final Color soft = light ? colorInkSoft : colorWhite.withValues(alpha: 0.5);
|
||||||
light ? colorInkSoft : colorWhite.withValues(alpha: 0.5);
|
|
||||||
|
|
||||||
final Widget header = SafeArea(
|
final Widget header = SafeArea(
|
||||||
bottom: false,
|
bottom: false,
|
||||||
@@ -80,8 +83,7 @@ class Sheet extends StatelessWidget {
|
|||||||
? null
|
? null
|
||||||
: Align(
|
: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: glassCircle(
|
child: glassCircle(CupertinoIcons.arrow_left, onBack!,
|
||||||
CupertinoIcons.arrow_left, onBack!,
|
|
||||||
onDark: !light),
|
onDark: !light),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -136,14 +138,14 @@ class Sheet extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: light ? colorFrost : colorPrimaryLight,
|
color: light ? colorFrost : colorPrimaryLight,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: scrollable
|
child: scrollable
|
||||||
? SingleChildScrollView(
|
? SingleChildScrollView(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 28, 20, 40),
|
padding: EdgeInsets.fromLTRB(20, 28, 20, bottomSpace),
|
||||||
child: child,
|
child: child,
|
||||||
)
|
)
|
||||||
: Padding(
|
: Padding(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Widget roundedCornerButton(
|
|||||||
Color? foreground,
|
Color? foreground,
|
||||||
IconData? icon,
|
IconData? icon,
|
||||||
bool enabled = true,
|
bool enabled = true,
|
||||||
double radius = 99,
|
double radius = 14,
|
||||||
double verticalPadding = 16,
|
double verticalPadding = 16,
|
||||||
}) {
|
}) {
|
||||||
final Color bg = enabled ? (background ?? colorNight) : colorGrey;
|
final Color bg = enabled ? (background ?? colorNight) : colorGrey;
|
||||||
@@ -27,7 +27,7 @@ Widget roundedCornerButton(
|
|||||||
disabledBackgroundColor: colorGrey.withValues(alpha: 0.4),
|
disabledBackgroundColor: colorGrey.withValues(alpha: 0.4),
|
||||||
padding: EdgeInsets.symmetric(vertical: verticalPadding),
|
padding: EdgeInsets.symmetric(vertical: verticalPadding),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(radius),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -55,7 +55,7 @@ Widget outlinedActionButton(
|
|||||||
Color? foreground,
|
Color? foreground,
|
||||||
IconData? icon,
|
IconData? icon,
|
||||||
bool enabled = true,
|
bool enabled = true,
|
||||||
double radius = 99,
|
double radius = 14,
|
||||||
}) {
|
}) {
|
||||||
final Color fg = enabled ? (foreground ?? colorPrimaryDark) : colorGrey;
|
final Color fg = enabled ? (foreground ?? colorPrimaryDark) : colorGrey;
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ Widget outlinedActionButton(
|
|||||||
side: BorderSide(color: fg.withValues(alpha: 0.35), width: 1),
|
side: BorderSide(color: fg.withValues(alpha: 0.35), width: 1),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(radius),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -112,7 +112,7 @@ Widget iconButton(
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: background ?? Colors.transparent,
|
color: background ?? Colors.transparent,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(radius),
|
||||||
border: bordered
|
border: bordered
|
||||||
? Border.all(color: borderColor ?? colorBorder, width: 1)
|
? Border.all(color: borderColor ?? colorBorder, width: 1)
|
||||||
: null,
|
: null,
|
||||||
@@ -161,7 +161,7 @@ Widget segmentedSelector<T>({
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? active : colorWhite,
|
color: isSelected ? active : colorWhite,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected ? active : colorBorder,
|
color: isSelected ? active : colorBorder,
|
||||||
width: 1,
|
width: 1,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ Widget countStepper(
|
|||||||
padding: const EdgeInsets.fromLTRB(14, 10, 8, 10),
|
padding: const EdgeInsets.fromLTRB(14, 10, 8, 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
border: Border.all(color: colorBorder, width: 1),
|
border: Border.all(color: colorBorder, width: 1),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -54,7 +54,7 @@ Widget _stepButton(IconData icon, VoidCallback? onTap) {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: enabled ? colorSecondary : colorInset,
|
color: enabled ? colorSecondary : colorInset,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Icon(icon,
|
child: Icon(icon,
|
||||||
size: 14, color: enabled ? colorPrimaryDark : colorGrey),
|
size: 14, color: enabled ? colorPrimaryDark : colorGrey),
|
||||||
|
|||||||
@@ -54,19 +54,19 @@ Widget inputField(
|
|||||||
contentPadding:
|
contentPadding:
|
||||||
const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
|
const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
borderSide: BorderSide(color: colorBorder, width: 1),
|
borderSide: BorderSide(color: colorBorder, width: 1),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
borderSide: BorderSide(color: colorPrimaryDark, width: 1.4),
|
borderSide: BorderSide(color: colorPrimaryDark, width: 1.4),
|
||||||
),
|
),
|
||||||
errorBorder: OutlineInputBorder(
|
errorBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
borderSide: BorderSide(color: colorNegative, width: 1),
|
borderSide: BorderSide(color: colorNegative, width: 1),
|
||||||
),
|
),
|
||||||
focusedErrorBorder: OutlineInputBorder(
|
focusedErrorBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
borderSide: BorderSide(color: colorNegative, width: 1.4),
|
borderSide: BorderSide(color: colorNegative, width: 1.4),
|
||||||
),
|
),
|
||||||
errorStyle: TextStyle(
|
errorStyle: TextStyle(
|
||||||
@@ -163,19 +163,19 @@ Widget excuseField(
|
|||||||
fillColor: colorWhite,
|
fillColor: colorWhite,
|
||||||
contentPadding: const EdgeInsets.all(14),
|
contentPadding: const EdgeInsets.all(14),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
borderSide: BorderSide(color: colorBorder, width: 1),
|
borderSide: BorderSide(color: colorBorder, width: 1),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
borderSide: BorderSide(color: colorPrimaryDark, width: 1.4),
|
borderSide: BorderSide(color: colorPrimaryDark, width: 1.4),
|
||||||
),
|
),
|
||||||
errorBorder: OutlineInputBorder(
|
errorBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
borderSide: BorderSide(color: colorNegative, width: 1),
|
borderSide: BorderSide(color: colorNegative, width: 1),
|
||||||
),
|
),
|
||||||
focusedErrorBorder: OutlineInputBorder(
|
focusedErrorBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
borderSide: BorderSide(color: colorNegative, width: 1.4),
|
borderSide: BorderSide(color: colorNegative, width: 1.4),
|
||||||
),
|
),
|
||||||
errorStyle: TextStyle(
|
errorStyle: TextStyle(
|
||||||
@@ -212,7 +212,7 @@ Widget selectField(
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
border: Border.all(color: colorBorder, width: 1),
|
border: Border.all(color: colorBorder, width: 1),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|||||||
@@ -443,8 +443,8 @@ class NewCommitmentState extends State<NewCommitment>
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorSheetBackground,
|
color: colorSheetBackground,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@@ -460,7 +460,7 @@ class NewCommitmentState extends State<NewCommitment>
|
|||||||
margin: const EdgeInsets.only(bottom: 20),
|
margin: const EdgeInsets.only(bottom: 20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorGrey.withValues(alpha: 0.35),
|
color: colorGrey.withValues(alpha: 0.35),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ class ExcuseReportState extends State<ExcuseReport>
|
|||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(14),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorInset,
|
color: colorInset,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
border: Border.all(color: colorBorder, width: 1),
|
border: Border.all(color: colorBorder, width: 1),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ class GoalDetailState extends State<GoalDetail>
|
|||||||
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite.withValues(alpha: 0.08),
|
color: colorWhite.withValues(alpha: 0.08),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -405,7 +405,7 @@ class GoalDetailState extends State<GoalDetail>
|
|||||||
width: 58,
|
width: 58,
|
||||||
height: 58,
|
height: 58,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: SceneArt(
|
child: SceneArt(
|
||||||
palette: scenePalette(routine.kind),
|
palette: scenePalette(routine.kind),
|
||||||
seed: sceneSeed(routine.title),
|
seed: sceneSeed(routine.title),
|
||||||
@@ -454,6 +454,7 @@ class GoalDetailState extends State<GoalDetail>
|
|||||||
color: routine.kind == RoutineKind.MustDo
|
color: routine.kind == RoutineKind.MustDo
|
||||||
? colorPrimaryDark
|
? colorPrimaryDark
|
||||||
: tone,
|
: tone,
|
||||||
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(CupertinoIcons.play_fill,
|
child: Icon(CupertinoIcons.play_fill,
|
||||||
size: 15, color: colorWhite),
|
size: 15, color: colorWhite),
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ class GoalEditorState extends State<GoalEditor> implements ConnectGoalEditor {
|
|||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(18),
|
padding: const EdgeInsets.all(18),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
@@ -338,6 +338,7 @@ class GoalEditorState extends State<GoalEditor> implements ConnectGoalEditor {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colour,
|
color: colour,
|
||||||
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: selected ? colorWhite : Colors.transparent,
|
color: selected ? colorWhite : Colors.transparent,
|
||||||
width: 3,
|
width: 3,
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'GoalsState.dart';
|
import 'GoalsState.dart';
|
||||||
|
|
||||||
class Goals extends StatefulWidget {
|
class Goals extends StatefulWidget {
|
||||||
const Goals({super.key});
|
/// Shown as a tab inside Home: no back arrow, and room at the bottom
|
||||||
|
/// for the nav.
|
||||||
|
final bool tab;
|
||||||
|
|
||||||
|
const Goals({super.key, this.tab = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<Goals> createState() => GoalsState();
|
State<Goals> createState() => GoalsState();
|
||||||
|
|||||||
@@ -74,7 +74,8 @@ class GoalsState extends State<Goals> implements ConnectGoals {
|
|||||||
return Sheet(
|
return Sheet(
|
||||||
eyebrow: "Grounded",
|
eyebrow: "Grounded",
|
||||||
title: "Goals",
|
title: "Goals",
|
||||||
onBack: _onBack,
|
onBack: widget.tab ? null : _onBack,
|
||||||
|
bottomSpace: widget.tab ? 130 : 40,
|
||||||
action: chromeAction(CupertinoIcons.add, _onNewGoal),
|
action: chromeAction(CupertinoIcons.add, _onNewGoal),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@@ -120,7 +121,7 @@ class GoalsState extends State<Goals> implements ConnectGoals {
|
|||||||
height: 260,
|
height: 260,
|
||||||
margin: const EdgeInsets.only(bottom: 14),
|
margin: const EdgeInsets.only(bottom: 14),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(16),
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Color(0x262A3A30),
|
color: Color(0x262A3A30),
|
||||||
@@ -130,7 +131,7 @@ class GoalsState extends State<Goals> implements ConnectGoals {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
@@ -151,7 +152,7 @@ class GoalsState extends State<Goals> implements ConnectGoals {
|
|||||||
children: [
|
children: [
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 10, vertical: 5),
|
horizontal: 10, vertical: 5),
|
||||||
child: text(
|
child: text(
|
||||||
|
|||||||
@@ -119,8 +119,8 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
|||||||
final GroupMessage? older = index + 1 < newestFirst.length
|
final GroupMessage? older = index + 1 < newestFirst.length
|
||||||
? newestFirst[index + 1]
|
? newestFirst[index + 1]
|
||||||
: null;
|
: null;
|
||||||
final bool newDay = older == null ||
|
final bool newDay =
|
||||||
!_sameDay(older.sentAt, message.sentAt);
|
older == null || !_sameDay(older.sentAt, message.sentAt);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@@ -145,6 +145,7 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
|||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
@@ -198,9 +199,12 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
|||||||
child: Center(
|
child: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorWhite.withValues(alpha: 0.7),
|
color: colorWhite.withValues(alpha: 0.7),
|
||||||
child: text(_dayName(moment), 11, TextType.Medium,
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
color: colorInkSoft),
|
),
|
||||||
|
child:
|
||||||
|
text(_dayName(moment), 11, TextType.Medium, color: colorInkSoft),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -241,7 +245,10 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
|||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.fromLTRB(14, 11, 14, 10),
|
padding: const EdgeInsets.fromLTRB(14, 11, 14, 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorNight,
|
color: colorNight,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -273,6 +280,7 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.fromLTRB(14, 10, 14, 10),
|
padding: const EdgeInsets.fromLTRB(14, 10, 14, 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
border: Border.all(color: colorGlassBorder),
|
border: Border.all(color: colorGlassBorder),
|
||||||
),
|
),
|
||||||
@@ -308,6 +316,7 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 12, 14, 12),
|
padding: const EdgeInsets.fromLTRB(12, 12, 14, 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
color: checkIn ? colorStandingGoodBg : colorOverdueWarmEnd,
|
color: checkIn ? colorStandingGoodBg : colorOverdueWarmEnd,
|
||||||
border: Border(
|
border: Border(
|
||||||
left: BorderSide(
|
left: BorderSide(
|
||||||
@@ -357,7 +366,10 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
|||||||
|
|
||||||
Widget _composer() {
|
Widget _composer() {
|
||||||
return Container(
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
0, 10, 0, MediaQuery.of(context).padding.bottom + 10),
|
0, 10, 0, MediaQuery.of(context).padding.bottom + 10),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -375,7 +387,10 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorAmbientMid,
|
color: colorAmbientMid,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: text(_quickReplies[index], 13, TextType.Regular,
|
child: text(_quickReplies[index], 13, TextType.Regular,
|
||||||
color: colorInk),
|
color: colorInk),
|
||||||
),
|
),
|
||||||
@@ -396,7 +411,10 @@ class GroupChatState extends State<GroupChat> implements ConnectGroupChat {
|
|||||||
child: Container(
|
child: Container(
|
||||||
width: 50,
|
width: 50,
|
||||||
height: 50,
|
height: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorNight,
|
color: colorNight,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Icon(CupertinoIcons.paperplane_fill,
|
child: Icon(CupertinoIcons.paperplane_fill,
|
||||||
size: 20, color: colorLime),
|
size: 20, color: colorLime),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -137,8 +137,8 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
_amount.text = formatAmount(value).replaceAll(",", "");
|
_amount.text = formatAmount(value).replaceAll(",", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSubmitLog(BuildContext sheetContext, GroupGoal goal,
|
void _onSubmitLog(
|
||||||
GroupObjective objective) {
|
BuildContext sheetContext, GroupGoal goal, GroupObjective objective) {
|
||||||
final double amount = double.tryParse(_amount.text.trim()) ?? 0;
|
final double amount = double.tryParse(_amount.text.trim()) ?? 0;
|
||||||
if (amount <= 0) {
|
if (amount <= 0) {
|
||||||
return;
|
return;
|
||||||
@@ -258,7 +258,10 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorAmbientMid,
|
color: colorAmbientMid,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -292,6 +295,7 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 18, 16, 18),
|
padding: const EdgeInsets.fromLTRB(20, 18, 16, 18),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
@@ -317,16 +321,18 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
Icon(CupertinoIcons.chat_bubble_2_fill,
|
Icon(CupertinoIcons.chat_bubble_2_fill,
|
||||||
size: 16, color: colorLime),
|
size: 16, color: colorLime),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
text("Group chat", 14, TextType.Medium,
|
text("Group chat", 14, TextType.Medium, color: colorLime),
|
||||||
color: colorLime),
|
|
||||||
if (_group.unread > 0) ...[
|
if (_group.unread > 0) ...[
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 7, vertical: 3),
|
horizontal: 7, vertical: 3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorLime,
|
color: colorLime,
|
||||||
child: text("${_group.unread} new", 10,
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
TextType.Medium,
|
),
|
||||||
|
child: text(
|
||||||
|
"${_group.unread} new", 10, TextType.Medium,
|
||||||
color: colorNight),
|
color: colorNight),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -351,9 +357,12 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
Container(
|
Container(
|
||||||
width: 46,
|
width: 46,
|
||||||
height: 46,
|
height: 46,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorWhite.withValues(alpha: 0.12),
|
color: colorWhite.withValues(alpha: 0.12),
|
||||||
child: Icon(CupertinoIcons.arrow_right,
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
size: 20, color: colorWhite),
|
),
|
||||||
|
child:
|
||||||
|
Icon(CupertinoIcons.arrow_right, size: 20, color: colorWhite),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -409,7 +418,9 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
meter(goal.progress,
|
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),
|
const SizedBox(height: 8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -426,6 +437,7 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 14, 14, 16),
|
padding: const EdgeInsets.fromLTRB(20, 14, 14, 16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
border: Border(top: BorderSide(color: colorBorder)),
|
border: Border(top: BorderSide(color: colorBorder)),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -472,7 +484,10 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
onTap: () => _onLog(goal, objective),
|
onTap: () => _onLog(goal, objective),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorNight,
|
color: colorNight,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@@ -560,7 +575,10 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
child: Container(
|
child: Container(
|
||||||
width: 44,
|
width: 44,
|
||||||
height: 44,
|
height: 44,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: nudged ? colorAmbientMid : colorNight,
|
color: nudged ? colorAmbientMid : colorNight,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
nudged
|
nudged
|
||||||
? CupertinoIcons.checkmark
|
? CupertinoIcons.checkmark
|
||||||
@@ -579,13 +597,13 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
|
|
||||||
/// Log progress: an amount (with one-tap presets that suit the unit) and
|
/// Log progress: an amount (with one-tap presets that suit the unit) and
|
||||||
/// an optional note that rides along into the chat.
|
/// an optional note that rides along into the chat.
|
||||||
Widget _logSheet(BuildContext sheetContext, GroupGoal goal,
|
Widget _logSheet(
|
||||||
GroupObjective objective) {
|
BuildContext sheetContext, GroupGoal goal, GroupObjective objective) {
|
||||||
final List<double> presets = _presets(objective.unit);
|
final List<double> presets = _presets(objective.unit);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.fromLTRB(20, 16, 20,
|
padding: EdgeInsets.fromLTRB(
|
||||||
MediaQuery.of(sheetContext).viewInsets.bottom + 28),
|
20, 16, 20, MediaQuery.of(sheetContext).viewInsets.bottom + 28),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -612,9 +630,12 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorAmbientMid,
|
color: colorAmbientMid,
|
||||||
child: text("+${formatAmount(preset)}", 15,
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
TextType.Medium,
|
),
|
||||||
|
child: text(
|
||||||
|
"+${formatAmount(preset)}", 15, TextType.Medium,
|
||||||
color: colorInk),
|
color: colorInk),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -636,8 +657,8 @@ class GroupDetailState extends State<GroupDetail>
|
|||||||
const SizedBox(height: 14),
|
const SizedBox(height: 14),
|
||||||
inputField("Note", _note, hint: "Optional: posted with the check-in"),
|
inputField("Note", _note, hint: "Optional: posted with the check-in"),
|
||||||
const SizedBox(height: 22),
|
const SizedBox(height: 22),
|
||||||
roundedCornerButton("Log it",
|
roundedCornerButton(
|
||||||
() => _onSubmitLog(sheetContext, goal, objective),
|
"Log it", () => _onSubmitLog(sheetContext, goal, objective),
|
||||||
icon: CupertinoIcons.checkmark),
|
icon: CupertinoIcons.checkmark),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ class GroupEditorState extends State<GroupEditor>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_joining) {
|
if (_joining) {
|
||||||
_model?.join(JoinGroupRequest(inviteCode: _code.text.trim().toUpperCase()));
|
_model
|
||||||
|
?.join(JoinGroupRequest(inviteCode: _code.text.trim().toUpperCase()));
|
||||||
} else {
|
} else {
|
||||||
_model?.create(GroupRequest(
|
_model?.create(GroupRequest(
|
||||||
name: _name.text.trim(),
|
name: _name.text.trim(),
|
||||||
@@ -130,9 +131,8 @@ class GroupEditorState extends State<GroupEditor>
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
displayTitle(_joining
|
displayTitle(
|
||||||
? "Got a code?"
|
_joining ? "Got a code?" : "Who's keeping\nyou honest?"),
|
||||||
: "Who's keeping\nyou honest?"),
|
|
||||||
const SizedBox(height: 22),
|
const SizedBox(height: 22),
|
||||||
segmentedSelector<String>(
|
segmentedSelector<String>(
|
||||||
options: const <String>["Create", "Join"],
|
options: const <String>["Create", "Join"],
|
||||||
@@ -221,6 +221,7 @@ class GroupEditorState extends State<GroupEditor>
|
|||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
@@ -237,8 +238,7 @@ class GroupEditorState extends State<GroupEditor>
|
|||||||
size: 16, color: colorWhite.withValues(alpha: 0.7)),
|
size: 16, color: colorWhite.withValues(alpha: 0.7)),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
text("1 MEMBER", 9, TextType.Bold,
|
text("1 MEMBER", 9, TextType.Bold,
|
||||||
color: colorWhite.withValues(alpha: 0.6),
|
color: colorWhite.withValues(alpha: 0.6), letterSpacing: 1.2),
|
||||||
letterSpacing: 1.2),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 14),
|
const SizedBox(height: 14),
|
||||||
@@ -266,6 +266,7 @@ class GroupEditorState extends State<GroupEditor>
|
|||||||
height: 40,
|
height: 40,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
color: colour,
|
color: colour,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: selected ? colorWhite : Colors.transparent,
|
color: selected ? colorWhite : Colors.transparent,
|
||||||
|
|||||||
@@ -195,8 +195,7 @@ class GroupGoalEditorState extends State<GroupGoalEditor>
|
|||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 28),
|
const SizedBox(height: 28),
|
||||||
sectionBreak("Objectives",
|
sectionBreak("Objectives", caption: "what everyone logs toward"),
|
||||||
caption: "what everyone logs toward"),
|
|
||||||
text(
|
text(
|
||||||
"Break the goal into numbers people can add to: kilometres, sessions, pages, hours. Each member logs their share and the totals add up.",
|
"Break the goal into numbers people can add to: kilometres, sessions, pages, hours. Each member logs their share and the totals add up.",
|
||||||
12,
|
12,
|
||||||
@@ -288,7 +287,8 @@ class GroupGoalEditorState extends State<GroupGoalEditor>
|
|||||||
keyboard: const TextInputType.numberWithOptions(
|
keyboard: const TextInputType.numberWithOptions(
|
||||||
decimal: true),
|
decimal: true),
|
||||||
formatters: <TextInputFormatter>[
|
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';
|
import 'GroupsState.dart';
|
||||||
|
|
||||||
class Groups extends StatefulWidget {
|
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
|
@override
|
||||||
State<Groups> createState() => GroupsState();
|
State<Groups> createState() => GroupsState();
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ class GroupsState extends State<Groups> implements ConnectGroups {
|
|||||||
return Sheet(
|
return Sheet(
|
||||||
eyebrow: "Grounded",
|
eyebrow: "Grounded",
|
||||||
title: "Groups",
|
title: "Groups",
|
||||||
onBack: _onBack,
|
onBack: widget.tab ? null : _onBack,
|
||||||
|
bottomSpace: widget.tab ? 130 : 40,
|
||||||
action: chromeAction(CupertinoIcons.add, _onNewGroup),
|
action: chromeAction(CupertinoIcons.add, _onNewGroup),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@@ -133,6 +134,7 @@ class GroupsState extends State<Groups> implements ConnectGroups {
|
|||||||
height: 250,
|
height: 250,
|
||||||
margin: const EdgeInsets.only(bottom: 14),
|
margin: const EdgeInsets.only(bottom: 14),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Color(0x262A3A30),
|
color: Color(0x262A3A30),
|
||||||
@@ -141,6 +143,7 @@ class GroupsState extends State<Groups> implements ConnectGroups {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
@@ -175,7 +178,10 @@ class GroupsState extends State<Groups> implements ConnectGroups {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 10, vertical: 6),
|
horizontal: 10, vertical: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorLime,
|
color: colorLime,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ class ViewGroupChat extends ParentViewModel {
|
|||||||
|
|
||||||
connection.onMessagesLoaded(getGroupMessageList(response.data));
|
connection.onMessagesLoaded(getGroupMessageList(response.data));
|
||||||
} catch (e) {
|
} 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);
|
connection.onProgressLogged(Group.fromJson(response.data), summary);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e, () => logProgress(request, summary),
|
handleError(e, () => logProgress(request, summary), () => dismissError(),
|
||||||
() => dismissError(), "Retry");
|
"Retry");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
|
|
||||||
bool _weekly = false;
|
bool _weekly = false;
|
||||||
|
|
||||||
|
/// The open tab, and the tabs built so far.
|
||||||
|
int _tab = 0;
|
||||||
|
|
||||||
|
final Set<int> _opened = <int>{0};
|
||||||
|
|
||||||
Standing _standing = Standing.Good;
|
Standing _standing = Standing.Good;
|
||||||
|
|
||||||
@@ -183,28 +187,14 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
_model?.tick(item);
|
_model?.tick(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onOpenProgress() {
|
|
||||||
GroundedNavigation().navigateToPageWithData(const Progress(), context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onOpenReportCard() {
|
void _onOpenReportCard() {
|
||||||
GroundedNavigation().navigateToPage(
|
GroundedNavigation().navigateToPage(
|
||||||
NavigatorType.justOpen, const ReportCardScreen(), context);
|
NavigatorType.justOpen, const ReportCardScreen(), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onOpenGoals() async {
|
void _onOpenGoals() => _onNavGoals();
|
||||||
final result = await GroundedNavigation()
|
|
||||||
.navigateToPageWithData(const Goals(), context);
|
|
||||||
|
|
||||||
if (result == true) {
|
void _onOpenGroups() => _onNavGroups();
|
||||||
_model?.loadPlan();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onOpenGroups() async {
|
|
||||||
await GroundedNavigation().navigateToPageWithData(const Groups(), context);
|
|
||||||
_model?.loadGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onOpenGroupChat(Group group) async {
|
void _onOpenGroupChat(Group group) async {
|
||||||
await GroundedNavigation()
|
await GroundedNavigation()
|
||||||
@@ -224,11 +214,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
.navigateToPage(NavigatorType.justOpen, const Training(), context);
|
.navigateToPage(NavigatorType.justOpen, const Training(), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onOpenRoutines() async {
|
void _onOpenRoutines() => _onNavRoutines();
|
||||||
await GroundedNavigation()
|
|
||||||
.navigateToPageWithData(const Routines(), context);
|
|
||||||
_model?.loadRoutines();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onOpenKind(RoutineKind kind) async {
|
void _onOpenKind(RoutineKind kind) async {
|
||||||
await GroundedNavigation()
|
await GroundedNavigation()
|
||||||
@@ -260,7 +246,39 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
.navigateToPage(NavigatorType.justOpen, const ExcuseReport(), context);
|
.navigateToPage(NavigatorType.justOpen, const ExcuseReport(), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onNavToday() {}
|
/// The nav switches tabs in place; nothing is pushed. A tab is built the
|
||||||
|
/// first time it is opened and then kept, so it holds its scroll and data.
|
||||||
|
void _onSelectTab(int index) {
|
||||||
|
if (index == _tab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_tab = index;
|
||||||
|
_opened.add(index);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Back on Today: pick up whatever changed in the other tabs.
|
||||||
|
if (index == 0) {
|
||||||
|
_model?.loadRoutines();
|
||||||
|
_model?.loadGroups();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onNavToday() => _onSelectTab(0);
|
||||||
|
|
||||||
|
void _onNavRoutines() => _onSelectTab(1);
|
||||||
|
|
||||||
|
void _onNavGoals() => _onSelectTab(2);
|
||||||
|
|
||||||
|
void _onNavGroups() => _onSelectTab(3);
|
||||||
|
|
||||||
|
void _onNavProgress() => _onSelectTab(4);
|
||||||
|
|
||||||
|
void _onBackToToday(bool didPop, Object? result) {
|
||||||
|
if (!didPop) {
|
||||||
|
_onSelectTab(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _onShowToday() {
|
void _onShowToday() {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -288,8 +306,8 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
return FadeTransition(
|
return FadeTransition(
|
||||||
opacity: curve,
|
opacity: curve,
|
||||||
child: SlideTransition(
|
child: SlideTransition(
|
||||||
position: Tween<Offset>(
|
position:
|
||||||
begin: const Offset(0, 0.12), end: Offset.zero)
|
Tween<Offset>(begin: const Offset(0, 0.12), end: Offset.zero)
|
||||||
.animate(curve),
|
.animate(curve),
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
@@ -389,14 +407,86 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
final bool queueIsHome =
|
final bool queueIsHome =
|
||||||
StandingEngine.showsOverdueQueueAsHome(_standing) && !_distressed;
|
StandingEngine.showsOverdueQueueAsHome(_standing) && !_distressed;
|
||||||
|
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
// Back on another tab returns to Today rather than leaving the app.
|
||||||
|
return PopScope(
|
||||||
|
canPop: _tab == 0,
|
||||||
|
onPopInvokedWithResult: _onBackToToday,
|
||||||
|
child: AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle.light,
|
value: SystemUiOverlayStyle.light,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: colorAmbientMid,
|
backgroundColor: colorAmbientMid,
|
||||||
body: AmbientBackground(
|
body: AmbientBackground(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
SingleChildScrollView(
|
IndexedStack(
|
||||||
|
index: _tab,
|
||||||
|
children: <Widget>[
|
||||||
|
_todayView(queueIsHome),
|
||||||
|
_opened.contains(1)
|
||||||
|
? const Routines(tab: true)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
_opened.contains(2)
|
||||||
|
? const Goals(tab: true)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
_opened.contains(3)
|
||||||
|
? const Groups(tab: true)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
_opened.contains(4)
|
||||||
|
? const Progress(tab: true)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: FloatingNav(
|
||||||
|
selected: _tab,
|
||||||
|
entries: <NavEntry>[
|
||||||
|
(
|
||||||
|
icon: CupertinoIcons.house_fill,
|
||||||
|
label: "Today",
|
||||||
|
onTap: _onNavToday
|
||||||
|
),
|
||||||
|
(
|
||||||
|
icon: CupertinoIcons.square_stack_3d_up_fill,
|
||||||
|
label: "Routines",
|
||||||
|
onTap: _onNavRoutines
|
||||||
|
),
|
||||||
|
(
|
||||||
|
icon: CupertinoIcons.flag_fill,
|
||||||
|
label: "Goals",
|
||||||
|
onTap: _onNavGoals
|
||||||
|
),
|
||||||
|
(
|
||||||
|
icon: CupertinoIcons.person_3_fill,
|
||||||
|
label: "Groups",
|
||||||
|
onTap: _onNavGroups
|
||||||
|
),
|
||||||
|
(
|
||||||
|
icon: CupertinoIcons.chart_bar_alt_fill,
|
||||||
|
label: "Progress",
|
||||||
|
onTap: _onNavProgress
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).animate().fadeIn(duration: 400.ms, delay: 250.ms).slideY(
|
||||||
|
begin: 0.4,
|
||||||
|
end: 0,
|
||||||
|
duration: 500.ms,
|
||||||
|
delay: 250.ms,
|
||||||
|
curve: Curves.easeOutCubic),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Today tab: the forest header and the day under it.
|
||||||
|
Widget _todayView(bool queueIsHome) {
|
||||||
|
return SingleChildScrollView(
|
||||||
padding: const EdgeInsets.only(bottom: 130),
|
padding: const EdgeInsets.only(bottom: 130),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@@ -420,51 +510,6 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: FloatingNav(
|
|
||||||
selected: 0,
|
|
||||||
entries: <NavEntry>[
|
|
||||||
(
|
|
||||||
icon: CupertinoIcons.house_fill,
|
|
||||||
label: "Today",
|
|
||||||
onTap: _onNavToday
|
|
||||||
),
|
|
||||||
(
|
|
||||||
icon: CupertinoIcons.square_stack_3d_up_fill,
|
|
||||||
label: "Routines",
|
|
||||||
onTap: _onOpenRoutines
|
|
||||||
),
|
|
||||||
(
|
|
||||||
icon: CupertinoIcons.flag_fill,
|
|
||||||
label: "Goals",
|
|
||||||
onTap: _onOpenGoals
|
|
||||||
),
|
|
||||||
(
|
|
||||||
icon: CupertinoIcons.person_3_fill,
|
|
||||||
label: "Groups",
|
|
||||||
onTap: _onOpenGroups
|
|
||||||
),
|
|
||||||
(
|
|
||||||
icon: CupertinoIcons.chart_bar_alt_fill,
|
|
||||||
label: "Progress",
|
|
||||||
onTap: _onOpenProgress
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).animate().fadeIn(duration: 400.ms, delay: 250.ms).slideY(
|
|
||||||
begin: 0.4,
|
|
||||||
end: 0,
|
|
||||||
duration: 500.ms,
|
|
||||||
delay: 250.ms,
|
|
||||||
curve: Curves.easeOutCubic),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,6 +520,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
Widget _header() {
|
Widget _header() {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.vertical(bottom: Radius.circular(20)),
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
end: Alignment.bottomCenter,
|
end: Alignment.bottomCenter,
|
||||||
@@ -568,6 +614,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
color: colorHeaderPanel,
|
color: colorHeaderPanel,
|
||||||
border: Border.all(color: colorHeaderLine),
|
border: Border.all(color: colorHeaderLine),
|
||||||
),
|
),
|
||||||
@@ -589,7 +636,12 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
curve: Curves.easeOutCubic,
|
curve: Curves.easeOutCubic,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 13),
|
padding: const EdgeInsets.symmetric(vertical: 13),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
color: active ? colorHeaderSelected : colorHeaderSelected.withValues(alpha: 0),
|
decoration: BoxDecoration(
|
||||||
|
color: active
|
||||||
|
? colorHeaderSelected
|
||||||
|
: colorHeaderSelected.withValues(alpha: 0),
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: text(label, 16, active ? TextType.Medium : TextType.Regular,
|
child: text(label, 16, active ? TextType.Medium : TextType.Regular,
|
||||||
color: active ? colorWhite : colorHeaderMuted),
|
color: active ? colorWhite : colorHeaderMuted),
|
||||||
),
|
),
|
||||||
@@ -603,12 +655,19 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
final DateTime first = DateTime(now.year, now.month, now.day)
|
final DateTime first = DateTime(now.year, now.month, now.day)
|
||||||
.subtract(const Duration(days: 6));
|
.subtract(const Duration(days: 6));
|
||||||
const List<String> days = <String>[
|
const List<String> days = <String>[
|
||||||
"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
|
"Mon",
|
||||||
|
"Tue",
|
||||||
|
"Wed",
|
||||||
|
"Thu",
|
||||||
|
"Fri",
|
||||||
|
"Sat",
|
||||||
|
"Sun"
|
||||||
];
|
];
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.fromLTRB(18, 16, 18, 18),
|
padding: const EdgeInsets.fromLTRB(18, 16, 18, 18),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
color: colorHeaderPanel,
|
color: colorHeaderPanel,
|
||||||
border: Border.all(color: colorHeaderLine),
|
border: Border.all(color: colorHeaderLine),
|
||||||
),
|
),
|
||||||
@@ -621,8 +680,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
children: [
|
children: [
|
||||||
text("${_streak()}", 28, TextType.Medium, color: colorWhite),
|
text("${_streak()}", 28, TextType.Medium, color: colorWhite),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
text("day streak", 16, TextType.Regular,
|
text("day streak", 16, TextType.Regular, color: colorHeaderMuted),
|
||||||
color: colorHeaderMuted),
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Icon(Icons.eco, size: 32, color: colorLime),
|
Icon(Icons.eco, size: 32, color: colorLime),
|
||||||
],
|
],
|
||||||
@@ -733,10 +791,10 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
|
|
||||||
/// The week in numbers: the average kept, a bar per day, and the classes.
|
/// The week in numbers: the average kept, a bar per day, and the classes.
|
||||||
Widget _weeklyNumbers() {
|
Widget _weeklyNumbers() {
|
||||||
final Map<int, double> byDay = _week?.completionByWeekday ?? <int, double>{};
|
final Map<int, double> byDay =
|
||||||
final double average = byDay.isEmpty
|
_week?.completionByWeekday ?? <int, double>{};
|
||||||
? 0
|
final double average =
|
||||||
: byDay.values.reduce((a, b) => a + b) / byDay.length;
|
byDay.isEmpty ? 0 : byDay.values.reduce((a, b) => a + b) / byDay.length;
|
||||||
const List<String> letters = <String>["M", "T", "W", "T", "F", "S", "S"];
|
const List<String> letters = <String>["M", "T", "W", "T", "F", "S", "S"];
|
||||||
final DateTime now = DateTime.now();
|
final DateTime now = DateTime.now();
|
||||||
final List<int> order = <int>[
|
final List<int> order = <int>[
|
||||||
@@ -781,12 +839,17 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
duration: const Duration(milliseconds: 400),
|
duration: const Duration(milliseconds: 400),
|
||||||
curve: Curves.easeOutCubic,
|
curve: Curves.easeOutCubic,
|
||||||
height: 90 * (byDay[day] ?? 0).clamp(0.04, 1.0),
|
height: 90 * (byDay[day] ?? 0).clamp(0.04, 1.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: (byDay[day] ?? 0) >= _keptFloor
|
color: (byDay[day] ?? 0) >= _keptFloor
|
||||||
? colorBarElective
|
? colorBarElective
|
||||||
: colorHeaderLine,
|
: colorHeaderLine,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
text(letters[day - 1], 13,
|
text(
|
||||||
|
letters[day - 1],
|
||||||
|
13,
|
||||||
day == now.weekday
|
day == now.weekday
|
||||||
? TextType.Medium
|
? TextType.Medium
|
||||||
: TextType.Regular,
|
: TextType.Regular,
|
||||||
@@ -834,7 +897,9 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
return Container(
|
return Container(
|
||||||
width: 74,
|
width: 74,
|
||||||
height: 74,
|
height: 74,
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||||
border: Border.all(color: colorWhite, width: 2),
|
border: Border.all(color: colorWhite, width: 2),
|
||||||
),
|
),
|
||||||
child: Opacity(
|
child: Opacity(
|
||||||
@@ -899,7 +964,10 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
const SizedBox(height: 9),
|
const SizedBox(height: 9),
|
||||||
Container(
|
Container(
|
||||||
height: 7,
|
height: 7,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorHeaderLine,
|
color: colorHeaderLine,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: FractionallySizedBox(
|
child: FractionallySizedBox(
|
||||||
widthFactor: fraction.clamp(0.0, 1.0),
|
widthFactor: fraction.clamp(0.0, 1.0),
|
||||||
@@ -978,23 +1046,29 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
child: Container(
|
child: Container(
|
||||||
width: 54,
|
width: 54,
|
||||||
height: 54,
|
height: 54,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Icon(CupertinoIcons.xmark, size: 22, color: colorInk),
|
child: Icon(CupertinoIcons.xmark, size: 22, color: colorInk),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
padding: EdgeInsets.fromLTRB(
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
18, 14, 18, MediaQuery.of(sheetContext).padding.bottom + 22),
|
),
|
||||||
|
padding: EdgeInsets.fromLTRB(18, 14, 18,
|
||||||
|
MediaQuery.of(sheetContext).padding.bottom + 22),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Center(
|
||||||
child: Container(
|
child:
|
||||||
width: 40, height: 4, color: colorGrabber),
|
Container(width: 40, height: 4, color: colorGrabber),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 18),
|
const SizedBox(height: 18),
|
||||||
text("Add to today", 24, TextType.Medium,
|
text("Add to today", 24, TextType.Medium,
|
||||||
@@ -1023,12 +1097,12 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
"Must-do", _onAddPickMustDo)),
|
"Must-do", _onAddPickMustDo)),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _addTile(CupertinoIcons.flag,
|
child: _addTile(
|
||||||
"Goal", _onAddPickGoal)),
|
CupertinoIcons.flag, "Goal", _onAddPickGoal)),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _addTile(CupertinoIcons.person_3,
|
child: _addTile(CupertinoIcons.person_3, "Group",
|
||||||
"Group", _onAddPickGroup)),
|
_onAddPickGroup)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
@@ -1049,7 +1123,10 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 22),
|
padding: const EdgeInsets.symmetric(vertical: 22),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorAmbientMid,
|
color: colorAmbientMid,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@@ -1067,7 +1144,10 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 22),
|
padding: const EdgeInsets.symmetric(vertical: 22),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorAmbientMid,
|
color: colorAmbientMid,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@@ -1093,7 +1173,9 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
return Container(
|
return Container(
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
border: Border.all(color: colorWhite, width: 2),
|
border: Border.all(color: colorWhite, width: 2),
|
||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@@ -1101,8 +1183,8 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
children: [
|
children: [
|
||||||
SceneArt(palette: sceneWorkout, seed: sceneSeed(_user.name)),
|
SceneArt(palette: sceneWorkout, seed: sceneSeed(_user.name)),
|
||||||
Center(
|
Center(
|
||||||
child: text(initial, size * 0.4, TextType.Medium,
|
child:
|
||||||
color: colorWhite),
|
text(initial, size * 0.4, TextType.Medium, color: colorWhite),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -1195,7 +1277,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
padding: const EdgeInsets.fromLTRB(20, 24, 20, 24),
|
padding: const EdgeInsets.fromLTRB(20, 24, 20, 24),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorFrost,
|
color: colorFrost,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
border: Border.all(color: colorGlassBorder),
|
border: Border.all(color: colorGlassBorder),
|
||||||
),
|
),
|
||||||
child: child,
|
child: child,
|
||||||
@@ -1300,12 +1382,12 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
children: [
|
children: [
|
||||||
_category(sceneWorkout, kindIcon(RoutineKind.Workout), "Workout", 3,
|
_category(sceneWorkout, kindIcon(RoutineKind.Workout), "Workout", 3,
|
||||||
() => _onOpenKind(RoutineKind.Workout)),
|
() => _onOpenKind(RoutineKind.Workout)),
|
||||||
_category(sceneMeditation, kindIcon(RoutineKind.Meditation),
|
_category(sceneMeditation, kindIcon(RoutineKind.Meditation), "Meditate",
|
||||||
"Meditate", 5, () => _onOpenKind(RoutineKind.Meditation)),
|
5, () => _onOpenKind(RoutineKind.Meditation)),
|
||||||
_category(sceneMustDo, kindIcon(RoutineKind.MustDo), "Must-do", 8,
|
_category(sceneMustDo, kindIcon(RoutineKind.MustDo), "Must-do", 8,
|
||||||
() => _onOpenKind(RoutineKind.MustDo)),
|
() => _onOpenKind(RoutineKind.MustDo)),
|
||||||
_category(goalScene(goalAccents[4]), CupertinoIcons.flag_fill,
|
_category(goalScene(goalAccents[4]), CupertinoIcons.flag_fill, "Goals",
|
||||||
"Goals", 13, _onOpenGoals),
|
13, _onOpenGoals),
|
||||||
_category(goalScene(goalAccents[2]), CupertinoIcons.flame_fill,
|
_category(goalScene(goalAccents[2]), CupertinoIcons.flame_fill,
|
||||||
"Training", 21, _onOpenTraining),
|
"Training", 21, _onOpenTraining),
|
||||||
],
|
],
|
||||||
@@ -1324,6 +1406,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
width: 74,
|
width: 74,
|
||||||
height: 74,
|
height: 74,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
border: Border.all(color: colorWhite, width: 2.5),
|
border: Border.all(color: colorWhite, width: 2.5),
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@@ -1333,7 +1416,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: ClipRect(
|
child: ClipOval(
|
||||||
child: SceneArt(
|
child: SceneArt(
|
||||||
palette: palette,
|
palette: palette,
|
||||||
seed: seed,
|
seed: seed,
|
||||||
@@ -1376,7 +1459,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
child: Container(
|
child: Container(
|
||||||
width: 244,
|
width: 244,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Color(0x262A3A30),
|
color: Color(0x262A3A30),
|
||||||
@@ -1386,7 +1469,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
@@ -1407,7 +1490,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
children: [
|
children: [
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.all(Radius.circular(14)),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 10, vertical: 5),
|
horizontal: 10, vertical: 5),
|
||||||
child: text(
|
child: text(
|
||||||
@@ -1515,7 +1598,9 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
onTap: () => _onOpenGroupChat(group),
|
onTap: () => _onOpenGroupChat(group),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 250,
|
width: 250,
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@@ -1550,9 +1635,13 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 8, vertical: 4),
|
horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorLime,
|
color: colorLime,
|
||||||
child: text("${group.unread} new", 10,
|
borderRadius:
|
||||||
TextType.Medium,
|
BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
|
child: text(
|
||||||
|
"${group.unread} new", 10, TextType.Medium,
|
||||||
color: colorNight),
|
color: colorNight),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
@@ -1618,9 +1707,8 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
sectionBreak(
|
sectionBreak(
|
||||||
"To-do",
|
"To-do",
|
||||||
caption: "${open.length} open, no debt",
|
caption: "${open.length} open, no debt",
|
||||||
trailing: done.isEmpty
|
trailing:
|
||||||
? null
|
done.isEmpty ? null : _viewAll("Clear done", _onClearDoneTodos),
|
||||||
: _viewAll("Clear done", _onClearDoneTodos),
|
|
||||||
),
|
),
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
tint: colorGlassStrong,
|
tint: colorGlassStrong,
|
||||||
@@ -1645,7 +1733,10 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
child: Container(
|
child: Container(
|
||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorNight,
|
color: colorNight,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Icon(CupertinoIcons.add,
|
child: Icon(CupertinoIcons.add,
|
||||||
size: 22, color: colorLime),
|
size: 22, color: colorLime),
|
||||||
@@ -1691,6 +1782,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
width: 30,
|
width: 30,
|
||||||
height: 30,
|
height: 30,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
color: item.done ? colorPrimary : colorWhite,
|
color: item.done ? colorPrimary : colorWhite,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: item.done
|
color: item.done
|
||||||
@@ -1794,11 +1886,14 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
"Your standing will recover as the debt decays.",
|
"Your standing will recover as the debt decays.",
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
..._overdue.take(_standing == Standing.Lockdown ? 1 : _overdue.length)
|
..._overdue
|
||||||
|
.take(_standing == Standing.Lockdown ? 1 : _overdue.length)
|
||||||
.map(_commitmentRow),
|
.map(_commitmentRow),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
roundedCornerButton(
|
roundedCornerButton(
|
||||||
_standing == Standing.Lockdown ? "Deal with this one" : "Open the queue",
|
_standing == Standing.Lockdown
|
||||||
|
? "Deal with this one"
|
||||||
|
: "Open the queue",
|
||||||
_onOpenOverdue,
|
_onOpenOverdue,
|
||||||
background: standingColor(_standing),
|
background: standingColor(_standing),
|
||||||
icon: CupertinoIcons.arrow_right,
|
icon: CupertinoIcons.arrow_right,
|
||||||
@@ -1853,8 +1948,8 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
sectionBreak("Three things", caption: "that actually matter"),
|
sectionBreak("Three things", caption: "that actually matter"),
|
||||||
..._plan
|
..._plan
|
||||||
.where((item) =>
|
.where(
|
||||||
item.commitmentClass == CommitmentClass.NonNegotiable)
|
(item) => item.commitmentClass == CommitmentClass.NonNegotiable)
|
||||||
.take(3)
|
.take(3)
|
||||||
.map(_commitmentRow),
|
.map(_commitmentRow),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
@@ -1871,7 +1966,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
onTap: _onOpenOverdue,
|
onTap: _onOpenOverdue,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
gradient: const LinearGradient(
|
gradient: const LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
@@ -1894,6 +1989,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
height: 58,
|
height: 58,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
gradient: const RadialGradient(
|
gradient: const RadialGradient(
|
||||||
center: Alignment(-0.3, -0.4),
|
center: Alignment(-0.3, -0.4),
|
||||||
colors: <Color>[colorOverdueGlow, colorStandingGrounded],
|
colors: <Color>[colorOverdueGlow, colorStandingGrounded],
|
||||||
@@ -1943,7 +2039,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.fromLTRB(22, 22, 22, 24),
|
padding: const EdgeInsets.fromLTRB(22, 22, 22, 24),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
gradient: const LinearGradient(
|
gradient: const LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
@@ -1967,8 +2063,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
text("Pattern spotted", 12, TextType.Medium, color: colorLime),
|
text("Pattern spotted", 12, TextType.Medium, color: colorLime),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
text("${cluster.occurrences}× this month", 11,
|
text("${cluster.occurrences}× this month", 11, TextType.Regular,
|
||||||
TextType.Regular,
|
|
||||||
color: colorWhite.withValues(alpha: 0.5)),
|
color: colorWhite.withValues(alpha: 0.5)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -2017,7 +2112,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
width: 88,
|
width: 88,
|
||||||
height: 88,
|
height: 88,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
child: Opacity(
|
child: Opacity(
|
||||||
opacity: ticked ? 0.45 : 1,
|
opacity: ticked ? 0.45 : 1,
|
||||||
child: SceneArt(
|
child: SceneArt(
|
||||||
@@ -2059,8 +2154,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(CupertinoIcons.clock, size: 11,
|
Icon(CupertinoIcons.clock, size: 11, color: colorInkSoft),
|
||||||
color: colorInkSoft),
|
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: text(formatWindow(item), 11, TextType.Regular,
|
child: text(formatWindow(item), 11, TextType.Regular,
|
||||||
@@ -2069,11 +2163,9 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
overflow: TextOverflow.ellipsis),
|
overflow: TextOverflow.ellipsis),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Icon(CupertinoIcons.timer, size: 11,
|
Icon(CupertinoIcons.timer, size: 11, color: colorInkSoft),
|
||||||
color: colorInkSoft),
|
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
text(formatMinutes(item.estMinutes), 11,
|
text(formatMinutes(item.estMinutes), 11, TextType.Regular,
|
||||||
TextType.Regular,
|
|
||||||
color: colorInkSoft),
|
color: colorInkSoft),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -2101,6 +2193,7 @@ class HomeState extends State<Home> implements ConnectHome {
|
|||||||
width: 44,
|
width: 44,
|
||||||
height: 44,
|
height: 44,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
color: checked ? colorPrimary : colorWhite,
|
color: checked ? colorPrimary : colorWhite,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: checked ? colorPrimary : accent.withValues(alpha: 0.35),
|
color: checked ? colorPrimary : accent.withValues(alpha: 0.35),
|
||||||
|
|||||||
@@ -184,8 +184,8 @@ class LiveTaskState extends State<LiveTask>
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorSheetBackground,
|
color: colorSheetBackground,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
|||||||
@@ -107,8 +107,8 @@ class LoginState extends State<Login> implements ConnectLogin {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorPrimaryLight,
|
color: colorPrimaryLight,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
|
|||||||
@@ -147,8 +147,8 @@ class OverdueQueueState extends State<OverdueQueue>
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorSheetBackground,
|
color: colorSheetBackground,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@@ -166,7 +166,7 @@ class OverdueQueueState extends State<OverdueQueue>
|
|||||||
margin: const EdgeInsets.only(bottom: 20),
|
margin: const EdgeInsets.only(bottom: 20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorGrey.withValues(alpha: 0.35),
|
color: colorGrey.withValues(alpha: 0.35),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -242,8 +242,8 @@ class OverdueQueueState extends State<OverdueQueue>
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorSheetBackground,
|
color: colorSheetBackground,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@@ -261,7 +261,7 @@ class OverdueQueueState extends State<OverdueQueue>
|
|||||||
margin: const EdgeInsets.only(bottom: 20),
|
margin: const EdgeInsets.only(bottom: 20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorGrey.withValues(alpha: 0.35),
|
color: colorGrey.withValues(alpha: 0.35),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -343,8 +343,8 @@ class OverdueQueueState extends State<OverdueQueue>
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorSheetBackground,
|
color: colorSheetBackground,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@@ -360,7 +360,7 @@ class OverdueQueueState extends State<OverdueQueue>
|
|||||||
margin: const EdgeInsets.only(bottom: 20),
|
margin: const EdgeInsets.only(bottom: 20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorGrey.withValues(alpha: 0.35),
|
color: colorGrey.withValues(alpha: 0.35),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -504,7 +504,7 @@ class OverdueQueueState extends State<OverdueQueue>
|
|||||||
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite.withValues(alpha: 0.10),
|
color: colorWhite.withValues(alpha: 0.10),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class ParentViewModel extends ChangeNotifier {
|
|||||||
height: 64,
|
height: 64,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorPrimaryDark,
|
color: colorPrimaryDark,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
child: Icon(CupertinoIcons.wifi_slash,
|
child: Icon(CupertinoIcons.wifi_slash,
|
||||||
size: 26, color: colorWhite),
|
size: 26, color: colorWhite),
|
||||||
@@ -396,7 +396,7 @@ class ParentViewModel extends ChangeNotifier {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorStandingGroundedBg,
|
color: colorStandingGroundedBg,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
CupertinoIcons.exclamationmark_triangle_fill,
|
CupertinoIcons.exclamationmark_triangle_fill,
|
||||||
@@ -544,8 +544,8 @@ class ParentViewModel extends ChangeNotifier {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorSheetBackground,
|
color: colorSheetBackground,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -559,7 +559,7 @@ class ParentViewModel extends ChangeNotifier {
|
|||||||
margin: const EdgeInsets.only(top: 12, bottom: 8),
|
margin: const EdgeInsets.only(top: 12, bottom: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorGrey.withValues(alpha: 0.35),
|
color: colorGrey.withValues(alpha: 0.35),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -579,7 +579,7 @@ class ParentViewModel extends ChangeNotifier {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: actionBgColor,
|
color: actionBgColor,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(15),
|
||||||
),
|
),
|
||||||
child: Icon(actionIconData,
|
child: Icon(actionIconData,
|
||||||
size: 24, color: actionColor),
|
size: 24, color: actionColor),
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'ProgressState.dart';
|
import 'ProgressState.dart';
|
||||||
|
|
||||||
class Progress extends StatefulWidget {
|
class Progress extends StatefulWidget {
|
||||||
const Progress({super.key});
|
/// Shown as a tab inside Home: no back arrow, and room at the bottom
|
||||||
|
/// for the nav.
|
||||||
|
final bool tab;
|
||||||
|
|
||||||
|
const Progress({super.key, this.tab = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<Progress> createState() => ProgressState();
|
State<Progress> createState() => ProgressState();
|
||||||
|
|||||||
@@ -33,7 +33,13 @@ class ProgressState extends State<Progress> implements ConnectProgress {
|
|||||||
bool _loaded = false;
|
bool _loaded = false;
|
||||||
|
|
||||||
static const List<String> _weekLetters = <String>[
|
static const List<String> _weekLetters = <String>[
|
||||||
"M", "T", "W", "T", "F", "S", "S"
|
"M",
|
||||||
|
"T",
|
||||||
|
"W",
|
||||||
|
"T",
|
||||||
|
"F",
|
||||||
|
"S",
|
||||||
|
"S"
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -130,7 +136,8 @@ class ProgressState extends State<Progress> implements ConnectProgress {
|
|||||||
return Sheet(
|
return Sheet(
|
||||||
eyebrow: "Over time",
|
eyebrow: "Over time",
|
||||||
title: "Progress",
|
title: "Progress",
|
||||||
onBack: _onBack,
|
onBack: widget.tab ? null : _onBack,
|
||||||
|
bottomSpace: widget.tab ? 130 : 40,
|
||||||
action: chromeAction(CupertinoIcons.slider_horizontal_3, _onOpenTargets),
|
action: chromeAction(CupertinoIcons.slider_horizontal_3, _onOpenTargets),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@@ -175,7 +182,6 @@ class ProgressState extends State<Progress> implements ConnectProgress {
|
|||||||
"Weekly report card",
|
"Weekly report card",
|
||||||
_onOpenReportCard,
|
_onOpenReportCard,
|
||||||
icon: CupertinoIcons.doc_text,
|
icon: CupertinoIcons.doc_text,
|
||||||
radius: 0,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
],
|
],
|
||||||
@@ -190,7 +196,10 @@ class ProgressState extends State<Progress> implements ConnectProgress {
|
|||||||
onTap: _onOpenTargets,
|
onTap: _onOpenTargets,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.fromLTRB(18, 16, 14, 16),
|
padding: const EdgeInsets.fromLTRB(18, 16, 14, 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorNight,
|
color: colorNight,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(CupertinoIcons.scope, size: 22, color: colorLime),
|
Icon(CupertinoIcons.scope, size: 22, color: colorLime),
|
||||||
@@ -226,8 +235,7 @@ class ProgressState extends State<Progress> implements ConnectProgress {
|
|||||||
/// and the bars underneath.
|
/// and the bars underneath.
|
||||||
Widget _headline() {
|
Widget _headline() {
|
||||||
final int held = ProgressEngine.held(_current);
|
final int held = ProgressEngine.held(_current);
|
||||||
final int target =
|
final int target = ProgressEngine.routineTarget(_targets, _period, _start);
|
||||||
ProgressEngine.routineTarget(_targets, _period, _start);
|
|
||||||
final int delta = held - ProgressEngine.held(_previous);
|
final int delta = held - ProgressEngine.held(_previous);
|
||||||
|
|
||||||
return card(
|
return card(
|
||||||
@@ -270,14 +278,16 @@ class ProgressState extends State<Progress> implements ConnectProgress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _deltaChip(int delta) {
|
Widget _deltaChip(int delta) {
|
||||||
final Color tone = delta > 0
|
final Color tone =
|
||||||
? colorPositive
|
delta > 0 ? colorPositive : (delta < 0 ? colorNegative : colorInkSoft);
|
||||||
: (delta < 0 ? colorNegative : colorInkSoft);
|
|
||||||
final String sign = delta > 0 ? "+" : "";
|
final String sign = delta > 0 ? "+" : "";
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 5),
|
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: tone.withValues(alpha: 0.12),
|
color: tone.withValues(alpha: 0.12),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
child: text("$sign$delta vs $_previousNoun", 11, TextType.Medium,
|
child: text("$sign$delta vs $_previousNoun", 11, TextType.Medium,
|
||||||
color: tone),
|
color: tone),
|
||||||
);
|
);
|
||||||
@@ -319,9 +329,12 @@ class ProgressState extends State<Progress> implements ConnectProgress {
|
|||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Container(
|
Container(
|
||||||
height: future ? 4 : 6 + 84 * fraction,
|
height: future ? 4 : 6 + 84 * fraction,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: isCurrent
|
color: isCurrent
|
||||||
? colorNight
|
? colorNight
|
||||||
: (future ? colorMuted : colorChartGrid),
|
: (future ? colorMuted : colorChartGrid),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
text(
|
text(
|
||||||
@@ -343,8 +356,7 @@ class ProgressState extends State<Progress> implements ConnectProgress {
|
|||||||
|
|
||||||
Widget _activeTile() {
|
Widget _activeTile() {
|
||||||
final int minutes = ProgressEngine.minutes(_current);
|
final int minutes = ProgressEngine.minutes(_current);
|
||||||
final int target =
|
final int target = ProgressEngine.minuteTarget(_targets, _period, _start);
|
||||||
ProgressEngine.minuteTarget(_targets, _period, _start);
|
|
||||||
|
|
||||||
return _tile(
|
return _tile(
|
||||||
CupertinoIcons.clock,
|
CupertinoIcons.clock,
|
||||||
@@ -396,14 +408,20 @@ class ProgressState extends State<Progress> implements ConnectProgress {
|
|||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: 10),
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
padding: const EdgeInsets.fromLTRB(14, 12, 16, 12),
|
padding: const EdgeInsets.fromLTRB(14, 12, 16, 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: colorCard,
|
color: colorCard,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 42,
|
width: 42,
|
||||||
height: 42,
|
height: 42,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: kindColor(kind).withValues(alpha: 0.14),
|
color: kindColor(kind).withValues(alpha: 0.14),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Icon(kindIcon(kind), size: 20, color: kindColor(kind)),
|
child: Icon(kindIcon(kind), size: 20, color: kindColor(kind)),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 14),
|
const SizedBox(width: 14),
|
||||||
|
|||||||
@@ -166,7 +166,6 @@ class TargetSetupState extends State<TargetSetup>
|
|||||||
"Save targets",
|
"Save targets",
|
||||||
_onSave,
|
_onSave,
|
||||||
icon: CupertinoIcons.checkmark,
|
icon: CupertinoIcons.checkmark,
|
||||||
radius: 0,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
],
|
],
|
||||||
@@ -225,8 +224,8 @@ class TargetSetupState extends State<TargetSetup>
|
|||||||
if (_scheduled != _targets.routinesPerWeek)
|
if (_scheduled != _targets.routinesPerWeek)
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: _onMatchSchedule,
|
onTap: _onMatchSchedule,
|
||||||
child: text("Match it", 12, TextType.Medium,
|
child:
|
||||||
color: colorInk),
|
text("Match it", 12, TextType.Medium, color: colorInk),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -243,7 +242,10 @@ class TargetSetupState extends State<TargetSetup>
|
|||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: enabled ? colorNight : colorMuted,
|
color: enabled ? colorNight : colorMuted,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
child: Icon(icon, size: 20, color: enabled ? colorLime : colorGrey),
|
child: Icon(icon, size: 20, color: enabled ? colorLime : colorGrey),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,11 +17,9 @@ class ViewProgress extends ParentViewModel {
|
|||||||
/// The runs and targets are on the device, so there is no network guard
|
/// The runs and targets are on the device, so there is no network guard
|
||||||
/// or spinner. Until a target is saved, the schedule stands in for it.
|
/// or spinner. Until a target is saved, the schedule stands in for it.
|
||||||
void initialise() async {
|
void initialise() async {
|
||||||
final List<RoutineRunRecord> records =
|
final List<RoutineRunRecord> records = await getDataManager().getRunLog();
|
||||||
await getDataManager().getRunLog();
|
|
||||||
|
|
||||||
final ProgressTargets targets =
|
final ProgressTargets targets = await getDataManager().getProgressTargets();
|
||||||
await getDataManager().getProgressTargets();
|
|
||||||
|
|
||||||
if (!targets.set) {
|
if (!targets.set) {
|
||||||
try {
|
try {
|
||||||
@@ -31,8 +29,8 @@ class ViewProgress extends ParentViewModel {
|
|||||||
page: Pageable(0, 0, 50, 0),
|
page: Pageable(0, 0, 50, 0),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
final int scheduled = ProgressEngine.scheduledPerWeek(
|
final int scheduled =
|
||||||
getRoutineChainList(response.data));
|
ProgressEngine.scheduledPerWeek(getRoutineChainList(response.data));
|
||||||
if (scheduled > 0) {
|
if (scheduled > 0) {
|
||||||
targets.routinesPerWeek = scheduled;
|
targets.routinesPerWeek = scheduled;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ class ViewTargetSetup extends ParentViewModel {
|
|||||||
/// Targets are on the device; the routines are only needed to count the
|
/// Targets are on the device; the routines are only needed to count the
|
||||||
/// schedule and to place reminders, so their failure is quiet.
|
/// schedule and to place reminders, so their failure is quiet.
|
||||||
void initialise() async {
|
void initialise() async {
|
||||||
final ProgressTargets targets =
|
final ProgressTargets targets = await getDataManager().getProgressTargets();
|
||||||
await getDataManager().getProgressTargets();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await getDataManager().getMyRoutines(HistoryRequest(
|
final response = await getDataManager().getMyRoutines(HistoryRequest(
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class ReportCardScreenState extends State<ReportCardScreen>
|
|||||||
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite.withValues(alpha: 0.08),
|
color: colorWhite.withValues(alpha: 0.08),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -128,7 +128,7 @@ class ReportCardScreenState extends State<ReportCardScreen>
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: text(_report.grade, 26, TextType.Light,
|
child: text(_report.grade, 26, TextType.Light,
|
||||||
color: colorPrimaryDark),
|
color: colorPrimaryDark),
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ class RoutineBuilderState extends State<RoutineBuilder>
|
|||||||
height: 124,
|
height: 124,
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
gradient: selected
|
gradient: selected
|
||||||
? LinearGradient(
|
? LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
@@ -424,6 +424,7 @@ class RoutineBuilderState extends State<RoutineBuilder>
|
|||||||
color: selected
|
color: selected
|
||||||
? colorWhite.withValues(alpha: 0.16)
|
? colorWhite.withValues(alpha: 0.16)
|
||||||
: kindBackground(kind),
|
: kindBackground(kind),
|
||||||
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(kindIcon(kind),
|
child: Icon(kindIcon(kind),
|
||||||
size: 17, color: selected ? colorWhite : tone),
|
size: 17, color: selected ? colorWhite : tone),
|
||||||
@@ -469,7 +470,7 @@ class RoutineBuilderState extends State<RoutineBuilder>
|
|||||||
padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
|
padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: applied ? kindBackground(_routine.kind) : colorWhite,
|
color: applied ? kindBackground(_routine.kind) : colorWhite,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: applied
|
color: applied
|
||||||
? kindColor(_routine.kind).withValues(alpha: 0.4)
|
? kindColor(_routine.kind).withValues(alpha: 0.4)
|
||||||
@@ -514,6 +515,7 @@ class RoutineBuilderState extends State<RoutineBuilder>
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: on ? tone : colorWhite,
|
color: on ? tone : colorWhite,
|
||||||
|
shape: BoxShape.circle,
|
||||||
border: Border.all(color: on ? tone : colorBorder),
|
border: Border.all(color: on ? tone : colorBorder),
|
||||||
),
|
),
|
||||||
child: text(weekdayInitial(day), 13,
|
child: text(weekdayInitial(day), 13,
|
||||||
@@ -574,7 +576,7 @@ class RoutineBuilderState extends State<RoutineBuilder>
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: kindBackground(_routine.kind),
|
color: kindBackground(_routine.kind),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: text("${index + 1}", 12, TextType.Bold, color: tone),
|
child: text("${index + 1}", 12, TextType.Bold, color: tone),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -462,7 +462,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
Widget _totalPill() {
|
Widget _totalPill() {
|
||||||
return GlassSurface(
|
return GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 11),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 11),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -501,7 +501,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
track: colorWhite.withValues(alpha: 0.22),
|
track: colorWhite.withValues(alpha: 0.22),
|
||||||
child: GlassSurface(
|
child: GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 150,
|
width: 150,
|
||||||
height: 150,
|
height: 150,
|
||||||
@@ -557,7 +557,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
),
|
),
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(200),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 120 + 50 * t,
|
width: 120 + 50 * t,
|
||||||
height: 120 + 50 * t,
|
height: 120 + 50 * t,
|
||||||
@@ -593,7 +593,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
children: [
|
children: [
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -615,7 +615,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
|
||||||
child: text("Left app ${_run.backgroundedCount}×", 11,
|
child: text("Left app ${_run.backgroundedCount}×", 11,
|
||||||
TextType.Medium,
|
TextType.Medium,
|
||||||
@@ -732,6 +732,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
height: size,
|
height: size,
|
||||||
padding: const EdgeInsets.all(2.5),
|
padding: const EdgeInsets.all(2.5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
color: isCurrent ? colorNight : colorWhite,
|
color: isCurrent ? colorNight : colorWhite,
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@@ -741,7 +742,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: ClipRect(
|
child: ClipOval(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
@@ -798,7 +799,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
margin: const EdgeInsets.only(right: 4),
|
margin: const EdgeInsets.only(right: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: filled || active ? tone : tone.withValues(alpha: 0.15),
|
color: filled || active ? tone : tone.withValues(alpha: 0.15),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@@ -898,7 +899,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorNight,
|
color: colorNight,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Color(0x331A1F1C),
|
color: Color(0x331A1F1C),
|
||||||
@@ -960,8 +961,8 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: colorSheetBackground,
|
color: colorSheetBackground,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
@@ -989,7 +990,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
padding: const EdgeInsets.fromLTRB(12, 12, 14, 12),
|
padding: const EdgeInsets.fromLTRB(12, 12, 14, 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: current ? kindBackground(_run.chain.kind) : colorWhite,
|
color: current ? kindBackground(_run.chain.kind) : colorWhite,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: current ? tone.withValues(alpha: 0.35) : colorBorder,
|
color: current ? tone.withValues(alpha: 0.35) : colorBorder,
|
||||||
),
|
),
|
||||||
@@ -1002,6 +1003,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: step.completed ? tone : colorSecondary,
|
color: step.completed ? tone : colorSecondary,
|
||||||
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: step.completed
|
child: step.completed
|
||||||
? Icon(Icons.check_rounded,
|
? Icon(Icons.check_rounded,
|
||||||
@@ -1075,7 +1077,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
children: [
|
children: [
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 12, vertical: 7),
|
horizontal: 12, vertical: 7),
|
||||||
child: text(
|
child: text(
|
||||||
@@ -1099,7 +1101,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
track: colorWhite.withValues(alpha: 0.3),
|
track: colorWhite.withValues(alpha: 0.3),
|
||||||
child: GlassSurface(
|
child: GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 158,
|
width: 158,
|
||||||
height: 158,
|
height: 158,
|
||||||
@@ -1131,7 +1133,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 22),
|
const SizedBox(height: 22),
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(16),
|
||||||
tint: colorGlassStrong,
|
tint: colorGlassStrong,
|
||||||
padding: const EdgeInsets.fromLTRB(18, 16, 18, 16),
|
padding: const EdgeInsets.fromLTRB(18, 16, 18, 16),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -1200,7 +1202,7 @@ class RoutineRunnerState extends State<RoutineRunner>
|
|||||||
final int streak = ProgressEngine.streak(withThis, now);
|
final int streak = ProgressEngine.streak(withThis, now);
|
||||||
|
|
||||||
return GlassSurface(
|
return GlassSurface(
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(16),
|
||||||
tint: colorGlassStrong,
|
tint: colorGlassStrong,
|
||||||
padding: const EdgeInsets.fromLTRB(18, 14, 18, 14),
|
padding: const EdgeInsets.fromLTRB(18, 14, 18, 14),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ class Routines extends StatefulWidget {
|
|||||||
/// Opens pre-filtered to one kind; null shows the whole library.
|
/// Opens pre-filtered to one kind; null shows the whole library.
|
||||||
final RoutineKind? kind;
|
final RoutineKind? kind;
|
||||||
|
|
||||||
const Routines({super.key, this.kind});
|
/// Shown as a tab inside Home: no back arrow, and room at the bottom
|
||||||
|
/// for the nav.
|
||||||
|
final bool tab;
|
||||||
|
|
||||||
|
const Routines({super.key, this.kind, this.tab = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<Routines> createState() => RoutinesState();
|
State<Routines> createState() => RoutinesState();
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
backgroundColor: colorAmbientMid,
|
backgroundColor: colorAmbientMid,
|
||||||
body: AmbientBackground(
|
body: AmbientBackground(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.only(bottom: 40),
|
padding: EdgeInsets.only(bottom: widget.tab ? 130 : 40),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -265,6 +265,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
padding: const EdgeInsets.fromLTRB(16, 6, 16, 0),
|
padding: const EdgeInsets.fromLTRB(16, 6, 16, 0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
if (!widget.tab)
|
||||||
glassCircle(CupertinoIcons.arrow_left, _onBack),
|
glassCircle(CupertinoIcons.arrow_left, _onBack),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
glassCircle(CupertinoIcons.add, _onBuild),
|
glassCircle(CupertinoIcons.add, _onBuild),
|
||||||
@@ -330,7 +331,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
..sort((a, b) => a.startMinute.compareTo(b.startMinute));
|
..sort((a, b) => a.startMinute.compareTo(b.startMinute));
|
||||||
|
|
||||||
return GlassSurface(
|
return GlassSurface(
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(16),
|
||||||
tint: colorGlassStrong,
|
tint: colorGlassStrong,
|
||||||
padding: const EdgeInsets.fromLTRB(18, 16, 18, 16),
|
padding: const EdgeInsets.fromLTRB(18, 16, 18, 16),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -402,6 +403,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
height: 78,
|
height: 78,
|
||||||
padding: const EdgeInsets.all(2.5),
|
padding: const EdgeInsets.all(2.5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
color: selected ? colorNight : colorWhite,
|
color: selected ? colorNight : colorWhite,
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@@ -411,7 +413,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: ClipRect(
|
child: ClipOval(
|
||||||
child: kind == null
|
child: kind == null
|
||||||
? Container(
|
? Container(
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
@@ -471,7 +473,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
child: Container(
|
child: Container(
|
||||||
width: 244,
|
width: 244,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(16),
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Color(0x262A3A30),
|
color: Color(0x262A3A30),
|
||||||
@@ -481,7 +483,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
@@ -502,7 +504,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
children: [
|
children: [
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 10, vertical: 5),
|
horizontal: 10, vertical: 5),
|
||||||
child: text(
|
child: text(
|
||||||
@@ -613,7 +615,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
width: 90,
|
width: 90,
|
||||||
height: 90,
|
height: 90,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
child: SceneArt(
|
child: SceneArt(
|
||||||
palette: scenePalette(routine.kind),
|
palette: scenePalette(routine.kind),
|
||||||
seed: sceneSeed(routine.title),
|
seed: sceneSeed(routine.title),
|
||||||
@@ -664,6 +666,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: tone,
|
color: tone,
|
||||||
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(Icons.check_rounded,
|
child: Icon(Icons.check_rounded,
|
||||||
size: 22, color: colorWhite),
|
size: 22, color: colorWhite),
|
||||||
@@ -715,8 +718,8 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: colorAmbientMid,
|
color: colorAmbientMid,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
@@ -753,7 +756,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
height: 4,
|
height: 4,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite.withValues(alpha: 0.7),
|
color: colorWhite.withValues(alpha: 0.7),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -770,7 +773,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
children: [
|
children: [
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 10, vertical: 5),
|
horizontal: 10, vertical: 5),
|
||||||
child: text(kindLabel(routine.kind), 10,
|
child: text(kindLabel(routine.kind), 10,
|
||||||
@@ -780,7 +783,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
onDark: true,
|
onDark: true,
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 10, vertical: 5),
|
horizontal: 10, vertical: 5),
|
||||||
child: text(
|
child: text(
|
||||||
@@ -814,7 +817,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
],
|
],
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
tint: colorGlassStrong,
|
tint: colorGlassStrong,
|
||||||
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -893,7 +896,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
padding: const EdgeInsets.fromLTRB(10, 10, 14, 10),
|
padding: const EdgeInsets.fromLTRB(10, 10, 14, 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite,
|
color: colorWhite,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -903,6 +906,7 @@ class RoutinesState extends State<Routines> implements ConnectRoutines {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: tone.withValues(alpha: 0.10),
|
color: tone.withValues(alpha: 0.10),
|
||||||
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: text("${index + 1}", 12, TextType.Medium, color: tone),
|
child: text("${index + 1}", 12, TextType.Medium, color: tone),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ class StepEditorState extends State<StepEditor> {
|
|||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: colorSheetBackground,
|
color: colorSheetBackground,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@@ -234,7 +234,7 @@ class StepEditorState extends State<StepEditor> {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: kindBackground(widget.kind),
|
color: kindBackground(widget.kind),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ class SettingsState extends State<Settings> implements ConnectSettings {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorSheetBackground,
|
color: colorSheetBackground,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.zero,
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.zero,
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@@ -118,7 +118,7 @@ class SettingsState extends State<Settings> implements ConnectSettings {
|
|||||||
margin: const EdgeInsets.only(bottom: 20),
|
margin: const EdgeInsets.only(bottom: 20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorGrey.withValues(alpha: 0.35),
|
color: colorGrey.withValues(alpha: 0.35),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -228,7 +228,7 @@ class SettingsState extends State<Settings> implements ConnectSettings {
|
|||||||
color: _amnestyRemaining > 0
|
color: _amnestyRemaining > 0
|
||||||
? colorStandingGoodBg
|
? colorStandingGoodBg
|
||||||
: colorMuted,
|
: colorMuted,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
CupertinoIcons.checkmark_shield_fill,
|
CupertinoIcons.checkmark_shield_fill,
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -299,11 +299,11 @@ class SplashState extends State<Splash> implements ConnectSplash {
|
|||||||
|
|
||||||
Widget _mark() {
|
Widget _mark() {
|
||||||
return GlassSurface(
|
return GlassSurface(
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(16),
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(16),
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: colorPrimary.withValues(alpha: 0.4),
|
color: colorPrimary.withValues(alpha: 0.4),
|
||||||
@@ -342,7 +342,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
|
|||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Color(0x142A3A30),
|
color: Color(0x142A3A30),
|
||||||
@@ -352,7 +352,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: GlassSurface(
|
child: GlassSurface(
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
tint: colorGlassStrong,
|
tint: colorGlassStrong,
|
||||||
padding: const EdgeInsets.fromLTRB(4, 4, 12, 4),
|
padding: const EdgeInsets.fromLTRB(4, 4, 12, 4),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -424,7 +424,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
|
|||||||
/// The whole idea in three beats, on glass over the valley.
|
/// The whole idea in three beats, on glass over the valley.
|
||||||
Widget _howItWorks() {
|
Widget _howItWorks() {
|
||||||
return GlassSurface(
|
return GlassSurface(
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(16),
|
||||||
tint: colorGlassStrong,
|
tint: colorGlassStrong,
|
||||||
padding: const EdgeInsets.fromLTRB(14, 14, 14, 14),
|
padding: const EdgeInsets.fromLTRB(14, 14, 14, 14),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -499,7 +499,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
|
|||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
radius: BorderRadius.zero,
|
radius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.all(3),
|
padding: const EdgeInsets.all(3),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 150,
|
width: 150,
|
||||||
@@ -510,7 +510,7 @@ class SplashState extends State<Splash> implements ConnectSplash {
|
|||||||
width: 150,
|
width: 150,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorNight,
|
color: colorNight,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
).animate().scaleX(
|
).animate().scaleX(
|
||||||
begin: 0.04,
|
begin: 0.04,
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class SessionExpiredState extends State<SessionExpired>
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite.withValues(alpha: 0.10),
|
color: colorWhite.withValues(alpha: 0.10),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
child: Icon(CupertinoIcons.lock_fill,
|
child: Icon(CupertinoIcons.lock_fill,
|
||||||
size: 24, color: colorWhite),
|
size: 24, color: colorWhite),
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class UpdateMeState extends State<UpdateMe> implements ConnectUpdateMe {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorWhite.withValues(alpha: 0.10),
|
color: colorWhite.withValues(alpha: 0.10),
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
child: Icon(CupertinoIcons.arrow_up_circle_fill,
|
child: Icon(CupertinoIcons.arrow_up_circle_fill,
|
||||||
size: 24, color: colorWhite),
|
size: 24, color: colorWhite),
|
||||||
|
|||||||
@@ -48,12 +48,13 @@ class GroundedApp extends StatelessWidget {
|
|||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
),
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.circular(14),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Material 3 rounds sheet tops by default; the app is square.
|
// Material 3 rounds sheet tops by default; the app is square.
|
||||||
bottomSheetTheme: const BottomSheetThemeData(
|
bottomSheetTheme: const BottomSheetThemeData(
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.zero),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
home: const Splash(),
|
home: const Splash(),
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ flutter:
|
|||||||
- assets/images/
|
- assets/images/
|
||||||
- .env
|
- .env
|
||||||
|
|
||||||
# General Sans (Fontshare, FFL) — see fonts/LICENSE-GeneralSans.txt
|
# Inter (SIL OFL) — see fonts/LICENSE-Inter.txt. Light is Inter Display Regular.
|
||||||
fonts:
|
fonts:
|
||||||
|
|
||||||
- family: GroundedBold
|
- family: GroundedBold
|
||||||
|
|||||||
Reference in New Issue
Block a user