From 17b22994ae27d0abd1da7f756f49b46248b8b4b7 Mon Sep 17 00:00:00 2001 From: Luke Walton Date: Wed, 6 Sep 2023 17:03:19 +0100 Subject: [PATCH 1/6] chore: Tidy, reorganise and prepare repo docs: update changelog and documentation feat(type): Add xSmall and conform to latest figma designs. --- .github/workflows/onMerge.yml | 8 +- .github/workflows/pr.yml | 8 +- .gitignore | 5 +- .pubignore | 2 + CHANGELOG.md | 144 +++----- CODE_OF_CONDUCT | 58 +++ CONTRIBUTING | 27 ++ README.md | 50 ++- analysis_options.yaml | 1 - custom_docs/components/Color/flutter.md | 102 ++++++ .../components/Grid/flutter.md | 0 .../components/Spacing/flutter.md | 0 .../components/Typography/flutter.md | 0 {docs => custom_docs}/flutter.template.md | 0 example/.gitignore | 44 --- example/README.md | 16 - example/ios/Podfile.lock | 13 +- example/ios/Runner.xcodeproj/project.pbxproj | 3 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- example/lib/home.dart | 73 ++++ example/lib/main.dart | 85 +---- example/lib/pages/color_example.dart | 43 ++- example/lib/pages/typography_example.dart | 8 +- example/macos/Podfile.lock | 2 +- .../macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- example/pubspec.lock | 88 ++--- example/test/color_test.dart | 8 +- example/test/typography_test.dart | 29 +- .../components/color_widgetbook.dart | 2 +- .../components/typography_widgetbook.dart | 1 - .../assets/fonts/IBMPlexSans-Light.otf | Bin .../assets/fonts/IBMPlexSans-Medium.otf | Bin .../assets/fonts/IBMPlexSans-Regular.otf | Bin lib/{ => src}/components/grid.dart | 10 + lib/{ => src}/components/spacing.dart | 8 + lib/{ => src}/components/text.dart | 330 +++++++----------- lib/{ => src}/theme/breakpoints.dart | 0 lib/{ => src}/theme/colors.dart | 87 +++-- lib/{ => src}/theme/colors_base.dart | 5 +- lib/{ => src}/theme/constants.dart | 0 lib/{ => src}/theme/theme.dart | 22 +- lib/{ => src}/tokens.dart | 2 +- lib/{ => src}/utils/extensions.dart | 0 lib/src/zeta.dart | 116 ++++++ lib/zeta.dart | 93 ----- lib/zeta_flutter.dart | 16 +- pubspec.yaml | 14 +- 48 files changed, 809 insertions(+), 720 deletions(-) create mode 100644 .pubignore create mode 100644 CODE_OF_CONDUCT create mode 100644 CONTRIBUTING create mode 100644 custom_docs/components/Color/flutter.md rename {docs => custom_docs}/components/Grid/flutter.md (100%) rename {docs => custom_docs}/components/Spacing/flutter.md (100%) rename {docs => custom_docs}/components/Typography/flutter.md (100%) rename {docs => custom_docs}/flutter.template.md (100%) delete mode 100644 example/.gitignore delete mode 100644 example/README.md create mode 100644 example/lib/home.dart rename lib/{ => src}/assets/fonts/IBMPlexSans-Light.otf (100%) rename lib/{ => src}/assets/fonts/IBMPlexSans-Medium.otf (100%) rename lib/{ => src}/assets/fonts/IBMPlexSans-Regular.otf (100%) rename lib/{ => src}/components/grid.dart (92%) rename lib/{ => src}/components/spacing.dart (95%) rename lib/{ => src}/components/text.dart (68%) rename lib/{ => src}/theme/breakpoints.dart (100%) rename lib/{ => src}/theme/colors.dart (91%) rename lib/{ => src}/theme/colors_base.dart (97%) rename lib/{ => src}/theme/constants.dart (100%) rename lib/{ => src}/theme/theme.dart (79%) rename lib/{ => src}/tokens.dart (99%) rename lib/{ => src}/utils/extensions.dart (100%) create mode 100644 lib/src/zeta.dart delete mode 100644 lib/zeta.dart diff --git a/.github/workflows/onMerge.yml b/.github/workflows/onMerge.yml index 01f42b4d..18736e3c 100644 --- a/.github/workflows/onMerge.yml +++ b/.github/workflows/onMerge.yml @@ -23,7 +23,7 @@ jobs: with: files: | **/*.dart - docs/* + custom-docs/* **/*.yaml **/*.yml files_ignore: example/* @@ -41,7 +41,7 @@ jobs: - name: Run step if any *.js file(s) or any file in the static folder change if: steps.changed-files-excluded.outputs.any_changed == 'true' run: | - echo "One or more *.js file(s) or any file in the static folder but not in the doc folder has changed." + echo "One or more *.js file(s) or any file in the static folder but not in the custom-docs folder has changed." echo "List all the files that have changed: ${{ steps.changed-files-excluded.outputs.all_changed_files }}" - name: Change flutter version tag uses: BentEngbers/flutter-change-version@v1.0.3 @@ -116,13 +116,13 @@ jobs: env: API_TOKEN_GITHUB: ${{ secrets.PAT }} with: - source_file: "docs/" + source_file: "custom-docs/" destination_repo: "zebratechnologies/zeta" destination_folder: "./" destination_branch: "flutter/${{ steps.read-version.outputs.version-number }}" user_email: "github-actions@github.com" user_name: "github-actions" - commit_message: "flutter docs" + commit_message: "flutter doc" - name: Open Zeta PR uses: thecanadianroot/open-pull-request-action@v1.0.3 with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index fdf9d3de..6b5bcf3a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -1,4 +1,4 @@ -name: "PR" # TODO: Publish this as a reusable action +name: "PR" on: pull_request: @@ -14,7 +14,7 @@ jobs: fetch-depth: 0 ref: main persist-credentials: false - - name: Get all changed *.dart, files in docs or pubspec.yaml + - name: Get all changed *.dart, files in custom-docs or pubspec.yaml id: changed-files uses: tj-actions/changed-files@v35 with: @@ -22,7 +22,7 @@ jobs: sha: ${{ github.event.pull_request.head.sha }} files: | **/*.dart - docs + custom-docs pubspec.yaml analyze: runs-on: ubuntu-latest @@ -49,7 +49,7 @@ jobs: uses: tj-actions/branch-names@v5.1 - uses: subosito/flutter-action@v2 with: - flutter-version: "3.10.x" + flutter-version: "3.13.x" channel: "stable" - name: Setup flutter run: flutter pub get diff --git a/.gitignore b/.gitignore index 98ad549a..f8ef7c9c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ migrate_working_dir/ # The .vscode folder contains launch configuration and tasks you configure in # VS Code which you may wish to be included in version control, so this line # is commented out by default. -#.vscode/ +.vscode/settings.json # Flutter/Dart/Pub related # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. @@ -31,4 +31,5 @@ build/ .vscode/settings.json .flutter-plugins -.flutter-plugins-dependencies \ No newline at end of file +.flutter-plugins-dependencies +**/.fvm/ \ No newline at end of file diff --git a/.pubignore b/.pubignore new file mode 100644 index 00000000..6c34b38e --- /dev/null +++ b/.pubignore @@ -0,0 +1,2 @@ +example/ +custom_docs/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 03da4e64..71a19d7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,131 +1,100 @@ -## [0.0.1+11] - 2023-08-09 -### :flying_saucer: Other Changes -- [`193dc42`](https://github.com/zebratechnologies/zeta-flutter/commit/193dc42c8e7419d9087afdffce0eae915af12819) - Feature/color ([#21](https://github.com/zebratechnologies/zeta-flutter/pull/21)) - -* feat(color): Adding color defs - -* feat(color): starting colorswatch util - -* bug(quality): updating lint rules - -* feat(color): adding widgetbook and tests - -* bug(platforms): adding windows into example - -* bug(type): Fixing reset height and tests failing *(commit by [@thelukewalton](https://github.com/thelukewalton))* +## [0.0.1+12] - 2023-09-06 +### :wrench: Chores +- [`6a2834e`](https://github.com/zebratechnologies/zeta-flutter/commit/6a2834e762c238d3927d83a239490250b1687b64) - Tidy, reorganise and prepare repo *(commit by [@thelukewalton](https://github.com/thelukewalton))* - -## [0.0.1+10] - 2023-07-11 ### :flying_saucer: Other Changes -- [`6638e94`](https://github.com/zebratechnologies/zeta-flutter/commit/6638e941b4027136c293c403c5c00e051fee5c97) - removing hardcoded shas *(PR [#19](https://github.com/zebratechnologies/zeta-flutter/pull/19) by [@thelukewalton](https://github.com/thelukewalton))* - +- [`f91e8ef`](https://github.com/zebratechnologies/zeta-flutter/commit/f91e8ef85c0a1670227d66bd441513bc33e6242c) - Feature/color ([#21](https://github.com/zebratechnologies/zeta-flutter/pull/21)) -## [0.0.1+9] - 2023-03-28 -### :memo: Documentation Changes -- [`fb835a4`](https://github.com/zebratechnologies/zeta-flutter/commit/fb835a43a94945989d5b0793d61894ea807bc745) - Updated spacing and grid documentation and edge cases *(PR [#11](https://github.com/zebratechnologies/zeta-flutter/pull/11) by [@thelukewalton](https://github.com/thelukewalton))* -- [`2a1cea3`](https://github.com/zebratechnologies/zeta-flutter/commit/2a1cea32d40c324cf36517cf05b5bb705d6eadb3) - Update typography documentation *(PR [#16](https://github.com/zebratechnologies/zeta-flutter/pull/16) by [@thelukewalton](https://github.com/thelukewalton))* -- [`79683b6`](https://github.com/zebratechnologies/zeta-flutter/commit/79683b6748e650e3bb61789e31e7ab7a7c153f88) - update docs *(PR [#17](https://github.com/zebratechnologies/zeta-flutter/pull/17) by [@thelukewalton](https://github.com/thelukewalton))* - -### :flying_saucer: Other Changes -- [`5a50e46`](https://github.com/zebratechnologies/zeta-flutter/commit/5a50e46f3500a9b186515305514839651576a444) - Update README.md ([#12](https://github.com/zebratechnologies/zeta-flutter/pull/12)) +* feat(color): Adding color defs -* Update README.md +* feat(color): starting colorswatch util -adding in tag to pass the CodeQL enablement exeption +* bug(quality): updating lint rules -* [automated commit] lint format and import sort +* feat(color): adding widgetbook and tests ---------- +* bug(platforms): adding windows into example -Co-authored-by: github-actions *(commit by [@knxp34](https://github.com/knxp34))* -- [`ffb9596`](https://github.com/zebratechnologies/zeta-flutter/commit/ffb9596ee04456147b87c2c35b3a08e8763bf7c2) - feat(typography) ([#14](https://github.com/zebratechnologies/zeta-flutter/pull/14)) - -* feat(typogrpahy): Initialising typography components - -* feat(typography): Added typography styles, text widget, widgetbook, docs, example app and tests *(commit by [@thelukewalton](https://github.com/thelukewalton))* -- [`591b757`](https://github.com/zebratechnologies/zeta-flutter/commit/591b7572ebf85da7510a8b6a3f9f8451dc93535a) - inject token to action; *(PR [#15](https://github.com/zebratechnologies/zeta-flutter/pull/15) by [@thelukewalton](https://github.com/thelukewalton))* -- [`69e6d5d`](https://github.com/zebratechnologies/zeta-flutter/commit/69e6d5da4b681293d0019d8bc143ada6478c1032) - Update README.md +* bug(type): Fixing reset height and tests failing *(commit by [@thelukewalton](https://github.com/thelukewalton))* -fixed the label *(commit by [@knxp34](https://github.com/knxp34))* -- [`1dcbcae`](https://github.com/zebratechnologies/zeta-flutter/commit/1dcbcaec2600210efcefc80861c29aaa7e44c27e) - removing hardcoded shas *(PR [#19](https://github.com/zebratechnologies/zeta-flutter/pull/19) by [@thelukewalton](https://github.com/thelukewalton))* +## 0.0.1+11 - 2023-08-09 -## [0.0.1+8] - 2023-03-27 -### :memo: Documentation Changes -- [`fb835a4`](https://github.com/zebratechnologies/zeta-flutter/commit/fb835a43a94945989d5b0793d61894ea807bc745) - Updated spacing and grid documentation and edge cases *(PR [#11](https://github.com/zebratechnologies/zeta-flutter/pull/11) by [@thelukewalton](https://github.com/thelukewalton))* -- [`2a1cea3`](https://github.com/zebratechnologies/zeta-flutter/commit/2a1cea32d40c324cf36517cf05b5bb705d6eadb3) - Update typography documentation *(PR [#16](https://github.com/zebratechnologies/zeta-flutter/pull/16) by [@thelukewalton](https://github.com/thelukewalton))* -- [`709f771`](https://github.com/zebratechnologies/zeta-flutter/commit/709f77185be705507475d90f044f94b2908fa5bb) - update docs *(PR [#17](https://github.com/zebratechnologies/zeta-flutter/pull/17) by [@thelukewalton](https://github.com/thelukewalton))* +### :sparkles: New Features -### :flying_saucer: Other Changes -- [`5a50e46`](https://github.com/zebratechnologies/zeta-flutter/commit/5a50e46f3500a9b186515305514839651576a444) - Update README.md ([#12](https://github.com/zebratechnologies/zeta-flutter/pull/12)) +- [`193dc42`](https://github.com/zebratechnologies/zeta-flutter/commit/193dc42c8e7419d9087afdffce0eae915af12819) - Color ([#21](https://github.com/zebratechnologies/zeta-flutter/pull/21)) _(commit by [@thelukewalton](https://github.com/thelukewalton))_ -* Update README.md + - [`a605819`](https://github.com/zebratechnologies/zeta-flutter/commit/a60581973764b5d06711fe6470f9963af934b7ad) - Adding color defs by [@thelukewalton](https://github.com/thelukewalton) + - [`f519cd8`](https://github.com/zebratechnologies/zeta-flutter/commit/f519cd856c7b4793ea7e24dc16f3abba0cffcf66) - starting colorswatch util by [@thelukewalton](https://github.com/thelukewalton) + - [`7445db0`](https://github.com/zebratechnologies/zeta-flutter/commit/7445db0b7da2434f5a55d3067369b3bd35df363b) - adding widgetbook and tests by [@thelukewalton](https://github.com/thelukewalton) -adding in tag to pass the CodeQL enablement exeption +### :bug: Bug Fixes -* [automated commit] lint format and import sort +- [`7529402`](https://github.com/zebratechnologies/zeta-flutter/commit/75294029f65d2a23cd41b5604165987fe434ea2e) - bug(quality): updating lint rules by [@thelukewalton](https://github.com/thelukewalton) +- [`3479adb`](https://github.com/zebratechnologies/zeta-flutter/commit/3479adb574c9ec1073552f888631f7cee12fe4cb) -bug(platforms): adding windows into example by [@thelukewalton](https://github.com/thelukewalton) +- [`70a6144`](https://github.com/zebratechnologies/zeta-flutter/commit/70a614446c4d526315eb3229478d89dbd1c031de) - bug(type): Fixing reset height and tests failing by [@thelukewalton](https://github.com/thelukewalton) ---------- +## 0.0.1+10 - 2023-07-11 -Co-authored-by: github-actions *(commit by [@knxp34](https://github.com/knxp34))* -- [`ffb9596`](https://github.com/zebratechnologies/zeta-flutter/commit/ffb9596ee04456147b87c2c35b3a08e8763bf7c2) - feat(typography) ([#14](https://github.com/zebratechnologies/zeta-flutter/pull/14)) +### :sparkles: New Features -* feat(typogrpahy): Initialising typography components +- [`546739c`](https://github.com/zebratechnologies/zeta-flutter/commit/546739c888e026b46546e22b3e1ea59c69e992d3) - Dimensions by [@thelukewalton](https://github.com/thelukewalton) -* feat(typography): Added typography styles, text widget, widgetbook, docs, example app and tests *(commit by [@thelukewalton](https://github.com/thelukewalton))* -- [`591b757`](https://github.com/zebratechnologies/zeta-flutter/commit/591b7572ebf85da7510a8b6a3f9f8451dc93535a) - inject token to action; *(PR [#15](https://github.com/zebratechnologies/zeta-flutter/pull/15) by [@thelukewalton](https://github.com/thelukewalton))* -- [`d591856`](https://github.com/zebratechnologies/zeta-flutter/commit/d59185680879bf2f938c4f2a6bd2328f29a3ddd2) - test *(commit by [@thelukewalton](https://github.com/thelukewalton))* +### :bug: Bug Fixes +- [`6638e94`](https://github.com/zebratechnologies/zeta-flutter/commit/6638e941b4027136c293c403c5c00e051fee5c97) - bug: Refactoring tokens by [@thelukewalton](https://github.com/thelukewalton) +- [`133a7ac`](https://github.com/zebratechnologies/zeta-flutter/commit/133a7acb3286af77a728479f8fafe9cef532130e) - bug: grid widgetbook hybrid example fix by [@thelukewalton](https://github.com/thelukewalton) -## [0.0.1+6] - Spacing - 2023-03-06 +- [`988964e`](https://github.com/zebratechnologies/zeta-flutter/commit/988964e122128c4f9e4423fd849b70b6283ccea7) - removing unused dependency; by [@thelukewalton](https://github.com/thelukewalton) -### :flying_saucer: Other Changes +## 0.0.1+9 - 2023-03-28 -- [`e29e53b`](https://github.com/zebratechnologies/zeta-flutter/commit/e29e53ba132cd155f2d40f4cfa6f6c3060558b4e) - fix(actions) another attempt at fixing actions checkout _(PR [#8](https://github.com/zebratechnologies/zeta-flutter/pull/8) by [@thelukewalton](https://github.com/thelukewalton))_ -- [`1dc0e1b`](https://github.com/zebratechnologies/zeta-flutter/commit/1dc0e1b64cb870685110516c5159b20fb903f2c3) - Update README.md _(commit by [@benken](https://github.com/benken))_ -- [`a2ca78e`](https://github.com/zebratechnologies/zeta-flutter/commit/a2ca78e863405f70b8199a889be3bc4f9c61ab1a) - Feature/spacing ([#9](https://github.com/zebratechnologies/zeta-flutter/pull/9)) +### :sparkles: New Features -* added spacing widget +- [`ffb9596`](https://github.com/zebratechnologies/zeta-flutter/commit/ffb9596ee04456147b87c2c35b3a08e8763bf7c2) - Typography _(commit by [@thelukewalton](https://github.com/thelukewalton))_ -* feature(Spacing): Added documentation and tests +### :memo: Documentation Changes -* bug: Updating left / right to start end. Potentially left / right could be added back. For now using start / end only _(commit by [@thelukewalton](https://github.com/thelukewalton))_ +- [`fb835a4`](https://github.com/zebratechnologies/zeta-flutter/commit/fb835a43a94945989d5b0793d61894ea807bc745) - Updated spacing and grid documentation and edge cases _(PR [#11](https://github.com/zebratechnologies/zeta-flutter/pull/11) by [@thelukewalton](https://github.com/thelukewalton))_ +- [`2a1cea3`](https://github.com/zebratechnologies/zeta-flutter/commit/2a1cea32d40c324cf36517cf05b5bb705d6eadb3) - Update typography documentation _(PR [#16](https://github.com/zebratechnologies/zeta-flutter/pull/16) by [@thelukewalton](https://github.com/thelukewalton))_ +- [`709f771`](https://github.com/zebratechnologies/zeta-flutter/commit/709f77185be705507475d90f044f94b2908fa5bb) - update docs _(PR [#17](https://github.com/zebratechnologies/zeta-flutter/pull/17) by [@thelukewalton](https://github.com/thelukewalton))_ -## [0.0.1+5] - Grid - 2023-02-17 +- [`5a50e46`](https://github.com/zebratechnologies/zeta-flutter/commit/5a50e46f3500a9b186515305514839651576a444) - Update README.md ([#12](https://github.com/zebratechnologies/zeta-flutter/pull/12)), Update README.md, adding in tag to pass the CodeQL enablement exeption, - [automated commit] lint format and import sort. Co-authored-by: github-actions _(commit by [@knxp34](https://github.com/knxp34))_ ### :bug: Bug Fixes -- [`f7a8d9a`](https://github.com/zebratechnologies/zeta-flutter/commit/f7a8d9a2ba078bf08fe80de07f6e9c871af9e451) - **actions**: Updated actions to push changelog to zeta. _(PR [#6](https://github.com/zebratechnologies/zeta-flutter/pull/6) by [@thelukewalton](https://github.com/thelukewalton))_ +- [`591b757`](https://github.com/zebratechnologies/zeta-flutter/commit/591b7572ebf85da7510a8b6a3f9f8451dc93535a) - inject token to action; _(PR [#15](https://github.com/zebratechnologies/zeta-flutter/pull/15) by [@thelukewalton](https://github.com/thelukewalton))_ +- [`d591856`](https://github.com/zebratechnologies/zeta-flutter/commit/d59185680879bf2f938c4f2a6bd2328f29a3ddd2) - test _(commit by [@thelukewalton](https://github.com/thelukewalton))_ +- [`1dcbcae`](https://github.com/zebratechnologies/zeta-flutter/commit/1dcbcaec2600210efcefc80861c29aaa7e44c27e) - removing hardcoded shas _(PR [#19](https://github.com/zebratechnologies/zeta-flutter/pull/19) by [@thelukewalton](https://github.com/thelukewalton))_ -### :flying_saucer: Other Changes - -- [`60527e8`](https://github.com/zebratechnologies/zeta-flutter/commit/60527e86da15b4a804990c7e67bae5c46d25dc7f) - Feature/grid ([#1](https://github.com/zebratechnologies/zeta-flutter/pull/1)) +## 0.0.1+6 - Spacing - 2023-03-06 -* setup grid +### :sparkles: New Features -* symmetrical grid using rows +- [`a2ca78e`](https://github.com/zebratechnologies/zeta-flutter/commit/a2ca78e863405f70b8199a889be3bc4f9c61ab1a) - Spacing ([#9](https://github.com/zebratechnologies/zeta-flutter/pull/9)) + _(commit by [@thelukewalton](https://github.com/thelukewalton))_ -* Asymmetrical +### :bug: Bug Fixes -* documentation +- [`e29e53b`](https://github.com/zebratechnologies/zeta-flutter/commit/e29e53ba132cd155f2d40f4cfa6f6c3060558b4e) - another attempt at fixing actions checkout _(PR [#8](https://github.com/zebratechnologies/zeta-flutter/pull/8) by [@thelukewalton](https://github.com/thelukewalton))_ -* widgetbook +### :memo: Documentation Changes -* Hyrbid +- [`1dc0e1b`](https://github.com/zebratechnologies/zeta-flutter/commit/1dc0e1b64cb870685110516c5159b20fb903f2c3) - Update README.md _(commit by [@benken](https://github.com/benken))_ -* actions _(commit by [@thelukewalton](https://github.com/thelukewalton))_ +## 0.0.1+5 - Grid - 2023-02-17 -- [`0340212`](https://github.com/zebratechnologies/zeta-flutter/commit/0340212963606fbe755aa94cbb98d38d663a5854) - fixing action ([#4](https://github.com/zebratechnologies/zeta-flutter/pull/4)) +### :sparkles: New Features -* fixing action +- [`60527e8`](https://github.com/zebratechnologies/zeta-flutter/commit/60527e86da15b4a804990c7e67bae5c46d25dc7f) - Grid ([#1](https://github.com/zebratechnologies/zeta-flutter/pull/1)) -* [automated commit] lint format and import sort +### :bug: Bug Fixes -Co-authored-by: github-actions _(commit by [@thelukewalton](https://github.com/thelukewalton))_ +- [`f7a8d9a`](https://github.com/zebratechnologies/zeta-flutter/commit/f7a8d9a2ba078bf08fe80de07f6e9c871af9e451) - **actions**: Updated actions to push changelog to zeta. _(PR [#6](https://github.com/zebratechnologies/zeta-flutter/pull/6) by [@thelukewalton](https://github.com/thelukewalton))_ - actions _(commit by [@thelukewalton](https://github.com/thelukewalton))_ -- [`b0ad7f1`](https://github.com/zebratechnologies/zeta-flutter/commit/b0ad7f12b8b583fb928d225ce9d1c1f3244046e5) - No ticket/code examples ([#5](https://github.com/zebratechnologies/zeta-flutter/pull/5)) +- [`0340212`](https://github.com/zebratechnologies/zeta-flutter/commit/0340212963606fbe755aa94cbb98d38d663a5854) - fixing action ([#4](https://github.com/zebratechnologies/zeta-flutter/pull/4)) -adding code example _(commit by [@thelukewalton](https://github.com/thelukewalton))_ +- [`b0ad7f1`](https://github.com/zebratechnologies/zeta-flutter/commit/b0ad7f12b8b583fb928d225ce9d1c1f3244046e5) - No ticket/code examples ([#5](https://github.com/zebratechnologies/zeta-flutter/pull/5))- adding code example _(commit by [@thelukewalton](https://github.com/thelukewalton))_ - [`4acf3c1`](https://github.com/zebratechnologies/zeta-flutter/commit/4acf3c1134b6c8d17827d8e2c665250d6f6ead1d) - fix(actions) Fix action refs _(PR [#7](https://github.com/zebratechnologies/zeta-flutter/pull/7) by [@thelukewalton](https://github.com/thelukewalton))_ - [`83e073b`](https://github.com/zebratechnologies/zeta-flutter/commit/83e073b16808d89373a74dba35172bb7a978e765) - fix(actions) another attempt at fixing actions checkout _(PR [#8](https://github.com/zebratechnologies/zeta-flutter/pull/8) by [@thelukewalton](https://github.com/thelukewalton))_ @@ -134,9 +103,4 @@ adding code example _(commit by [@thelukewalton](https://github.com/thelukewalto - Initial setup -[0.0.1+5]: https://github.com/zebratechnologies/zeta-flutter/compare/0.0.1+1...0.0.1+5 - -[0.0.1+8]: https://github.com/zebratechnologies/zeta-flutter/compare/0.0.1+6...0.0.1+8 -[0.0.1+9]: https://github.com/zebratechnologies/zeta-flutter/compare/0.0.1+6...0.0.1+9 -[0.0.1+10]: https://github.com/zebratechnologies/zeta-flutter/compare/0.0.1+9...0.0.1+10 -[0.0.1+11]: https://github.com/zebratechnologies/zeta-flutter/compare/0.0.1+10...0.0.1+11 \ No newline at end of file +[0.0.1+12]: https://github.com/zebratechnologies/zeta-flutter/compare/0.0.1+11...0.0.1+12 \ No newline at end of file diff --git a/CODE_OF_CONDUCT b/CODE_OF_CONDUCT new file mode 100644 index 00000000..fd77c7dc --- /dev/null +++ b/CODE_OF_CONDUCT @@ -0,0 +1,58 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. +Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +Devrel@zebra.com. +All complaints will be reviewed and investigated promptly and fairly. +The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the Contributor Covenant, +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. diff --git a/CONTRIBUTING b/CONTRIBUTING new file mode 100644 index 00000000..2ccbf54b --- /dev/null +++ b/CONTRIBUTING @@ -0,0 +1,27 @@ +# Getting Involved + +Thank you for your interest in this project. We'd love to see your contributions. There are just few small guidelines you need to follow. +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Opening an issue + +If you've noticed a bug or you have a suggestion for a new feature, please go ahead and open an issue in this project. Please do include as much information as possible. + +Please file issues before doing substantial work; this will ensure that others don't duplicate the work and that there's a chance to discuss any design issues. + +## Making a code change + +We're always open to pull requests, but these should be small and clearly described so that we can understand what you're trying to do. + +When you're ready to start coding, fork the needed repository to your own GitHub account and make your changes in a new branch. Once you're happy, open a pull request and explain what the change is and why you think we should include it in our project. + +## Code reviews + +All submissions, including submissions by project members, require review. We use GitHub pull requests (PRs) for this purpose. Consult [GitHub Help](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) for more information on using pull requests. + +Before a PR can be reviewed, ensure you have done the following, and fixed any issues that may arise: + +- Ensure branch is up to date `git rebase main` +- Check formatting: `flutter format .` +- Run static analyses: `flutter analyze` +- Run unit-tests: `flutter test` diff --git a/README.md b/README.md index 12f8ea07..b2b42122 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,35 @@ -
- Zebra Repository Information -
    -
  • Zebra Business Unit : DMO - Innovation & Design
  • -
  • Zebra Manager : ncvt73
  • -
  • Zebra Repo Admin: lwalton
  • -
  • Zebra Jira Project ID: N/A
  • -
  • Product: Zebra Design System (Zeta) - Flutter Component Library
  • -
  • Topics: no_codeql
  • -
-
- -# zeta-flutter - -Zebra Design System (Zeta) - Flutter Component Library +# Zeta Flutter + +Zeta is the new, formal, standardized Zebra Design System based off the successes of ZDS (Zebra Design System). + +Note: This package is in pre-release, and so many aspects are incomplete. + +# Usage + +[Install zeta_flutter](https://pub.dev/packages/zeta_flutter/install) + +Zeta extends the use of Flutter's built in theming tools, and so to work correctly your app needs to be wrapped with the zeta theme as such: + +```dart + + @override + Widget build(BuildContext context) { + return Zeta( + builder: (context, theme, colors) { + return MaterialApp.router(theme: theme, routerConfig: router); + }, + ); + } +``` + +This returns the Zeta theme and colors, which will be used across the app. Custom `ThemeData` and `ZetaColor` objects can be passed in to apply custom themes and colors. + +## Viewing the components + +To view examples of all the components in the library, you can run the example app in this repo or go to [Zeta](https://zeta-ds.web.app/) + +## Licensing + +This software is licensed with the MIT license (see [LICENSE](./LICENSE)). + +--- diff --git a/analysis_options.yaml b/analysis_options.yaml index dd2905be..b689dcb6 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -30,7 +30,6 @@ linter: avoid_types_on_closure_parameters: false cascade_invocations: false close_sinks: false - diagnostic_describe_all_properties: false lines_longer_than_80_chars: false omit_local_variable_types: false prefer_constructors_over_static_methods: false diff --git a/custom_docs/components/Color/flutter.md b/custom_docs/components/Color/flutter.md new file mode 100644 index 00000000..86eec97e --- /dev/null +++ b/custom_docs/components/Color/flutter.md @@ -0,0 +1,102 @@ +## ZetaColors Usage + +To import ZetaColors into a Dart file: + +```dart +import 'package:zeta_flutter/zeta_flutter.dart'; +``` + +### Example + +ZetaColors should be an app-wide parameter, to ensure colors throughout an app stay consistent. As such, it should be created at the highest level of an app, from where it can be distributed. + +```dart +import 'package:zeta_flutter/zeta_flutter.dart'; + +class MyApp extends StatelessWidget{ + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + + /// Build colors object with custom colors. + final ZetaColors colors = ZetaColors( + /// Add custom colors here. + ); + + /// Wrap whole app with [Zeta] to provide theming. + return Zeta( + colors: colors, + builder: (BuildContext context, ThemeData theme, ZetaColors colors) => ZetaColorExample(), + ); + } +} + +class ZetaColorExample extends StatelessWidget{ + const ZetaColorExample({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + + + final ZetaColors colors = ZetaColors.of(context); + + return Container( + color: colors.red, + ); + } +} + +``` + +### ZetaColors + +ZetaColors provides a full range of colors to be used, with various modifiers to return colors based on function. + +The following colors are provided as `ZetaColorSwatch`: + +- primary +- secondary +- cool (grey) +- warm (grey) +- blue +- red +- green +- orange +- purple +- yellow +- teal +- pink + +When creating a custom ZetaColors object, colors should be provided as a full `ZetaColorSwatch`, unless you want to use the default values, which are described in `ZetaColorBase`. + +ZetaColors has the following modifiers: + +- primary, onPrimary +- secondary, onSecondary +- positive, onPositive, negative, onNegative, warning, onWarning, info, onInfo +- surface, onSurface, surfaceDisabled, surfaceHovered, surfaceSecondary, surfaceTertiary, surfaceSelectedHovered, surfaceSelected +- background, onBackground +- textDefault, textSubtle, textDisabled, textInverse, +- borderDefault, borderSubtle, borderDisabled, borderSelected, +- white, black +- link, linkVisited, +- textLightMode, textDarkMode + +Commonly, `ZetaColors.toColorScheme` should be used to create a color scheme for use with `MaterialApp`. + +### ZetaColorSwatch + +ZetaColorSwatch returns a swatch of colors with shades at 10, 20, 30, 40, 50, 60, 70, 80, 90 and 100 where the higher the value, the darker the color. + +ZetaColorSwatch has the following modifiers: + +- primary +- icon +- border, borderSubtle +- surface +- subtle +- on +- disabled + +ZetaColorSwatch can be generated from a single color, but this is not recommended as results may not look as expected, and will probably not conform to any accessability requirements. diff --git a/docs/components/Grid/flutter.md b/custom_docs/components/Grid/flutter.md similarity index 100% rename from docs/components/Grid/flutter.md rename to custom_docs/components/Grid/flutter.md diff --git a/docs/components/Spacing/flutter.md b/custom_docs/components/Spacing/flutter.md similarity index 100% rename from docs/components/Spacing/flutter.md rename to custom_docs/components/Spacing/flutter.md diff --git a/docs/components/Typography/flutter.md b/custom_docs/components/Typography/flutter.md similarity index 100% rename from docs/components/Typography/flutter.md rename to custom_docs/components/Typography/flutter.md diff --git a/docs/flutter.template.md b/custom_docs/flutter.template.md similarity index 100% rename from docs/flutter.template.md rename to custom_docs/flutter.template.md diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index 24476c5d..00000000 --- a/example/.gitignore +++ /dev/null @@ -1,44 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release diff --git a/example/README.md b/example/README.md deleted file mode 100644 index 7e10926e..00000000 --- a/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# zeta_example - -Demonstrates how to use the zeta plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index ccaa3cc1..d403d6cb 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,22 +1,23 @@ PODS: - Flutter (1.0.0) - - zeta (0.0.1): + - path_provider_foundation (0.0.1): - Flutter + - FlutterMacOS DEPENDENCIES: - Flutter (from `Flutter`) - - zeta (from `.symlinks/plugins/zeta/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) EXTERNAL SOURCES: Flutter: :path: Flutter - zeta: - :path: ".symlinks/plugins/zeta/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - zeta: d56cf33a2716f24c37bd8e71ab754f8b79e9d602 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 1950e945..83853dd6 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -156,7 +156,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -227,6 +227,7 @@ files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a3..a6b826db 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ children; + Component(this.name, this.page, [this.children = const []]); +} + +final List components = [ + Component(GridExample.name, const GridExample()), + Component(SpacingExample.name, const SpacingExample()), + Component(TypographyExample.name, const TypographyExample()), + Component(ColorExample.name, const ColorExample()), +]; + +class Home extends StatefulWidget { + const Home({super.key}); + + @override + State createState() => _HomeState(); +} + +final GoRouter router = GoRouter( + routes: [ + GoRoute( + path: '/', + builder: (_, __) => const Home(), + routes: [ + ...components.map( + (e) => GoRoute( + path: e.name, + builder: (_, __) => e.page, + routes: e.children.map((f) => GoRoute(path: f.name, builder: (_, __) => f.page)).toList(), + ), + ) + ], + ), + ], +); + +class _HomeState extends State { + @override + Widget build(BuildContext context) { + final items = components..sort((a, b) => a.name.compareTo(b.name)); + + final colors = ZetaColors.of(context); + return Scaffold( + appBar: AppBar( + title: const Text('Zeta'), + backgroundColor: colors.primary, + ), + body: ColoredBox( + color: colors.background, + child: ListView.builder( + itemCount: items.length, + itemBuilder: (context, index) { + return ListTile( + title: ZetaText(items[index].name), + onTap: () => context.go('/${items[index].name}'), + ); + }, + ), + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 9b854b9e..b742e1db 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,54 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; -import 'pages/color_example.dart'; -import 'pages/grid_example.dart'; -import 'pages/spacing_example.dart'; -import 'pages/typography_example.dart'; +import 'home.dart'; -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatefulWidget { - const MyApp({super.key}); - - @override - State createState() => _MyAppState(); -} +void main() => runApp(const ZetaExample()); -class Component { - final String name; - final Widget page; - final List children; - Component(this.name, this.page, [this.children = const []]); -} - -final List components = [ - Component(GridExample.name, const GridExample()), - Component(SpacingExample.name, const SpacingExample()), - Component(TypographyExample.name, const TypographyExample()), - Component(ColorExample.name, const ColorExample()), -]; - -class _MyAppState extends State { - final GoRouter router = GoRouter( - routes: [ - GoRoute( - path: '/', - builder: (_, __) => const Home(), - routes: [ - ...components.map( - (e) => GoRoute( - path: e.name, - builder: (_, __) => e.page, - routes: e.children.map((f) => GoRoute(path: f.name, builder: (_, __) => f.page)).toList(), - ), - ) - ], - ), - ], - ); +class ZetaExample extends StatelessWidget { + const ZetaExample({super.key}); @override Widget build(BuildContext context) { @@ -59,37 +16,3 @@ class _MyAppState extends State { ); } } - -class Home extends StatefulWidget { - const Home({super.key}); - - @override - State createState() => _HomeState(); -} - -class _HomeState extends State { - @override - Widget build(BuildContext context) { - final items = components..sort((a, b) => a.name.compareTo(b.name)); - - final colors = ZetaColors.of(context); - return Scaffold( - appBar: AppBar( - title: const Text('Zeta'), - backgroundColor: colors.primary, - ), - body: ColoredBox( - color: colors.background, - child: ListView.builder( - itemCount: items.length, - itemBuilder: (context, index) { - return ListTile( - title: ZetaText(items[index].name), - onTap: () => context.go('/${items[index].name}'), - ); - }, - ), - ), - ); - } -} diff --git a/example/lib/pages/color_example.dart b/example/lib/pages/color_example.dart index bc576d1c..633b2519 100644 --- a/example/lib/pages/color_example.dart +++ b/example/lib/pages/color_example.dart @@ -102,7 +102,7 @@ class _ColorExampleState extends State { return ExampleScaffold( name: ColorExample.name, actions: [ - const Center(child: ZetaText('DarkMode', textColor: Colors.white)), + Center(child: ZetaText('DarkMode', textColor: Colors.white)), Switch.adaptive( value: colors.isDarkMode, onChanged: (isDarkMode) => ZetaColors.setColors(context, colors.copyWith(isDarkMode: isDarkMode)), @@ -123,7 +123,7 @@ class _ColorExampleState extends State { MyRow(children: backdrop, title: 'Backdrop colors'), MyRow(children: primaries, title: 'Primary colors'), MyRow(children: alerts, title: 'Alert colors'), - const Row(children: [ZetaText.displayMedium('Full color swatches')]).squish(Dimensions.x8), + Row(children: [ZetaText.displayMedium('Full color swatches')]).squish(Dimensions.x8), ...swatches.entries.map( (value) => Row( children: List.generate(10, (index) => 100 - (10 * index)) @@ -161,36 +161,41 @@ class _ColorExampleState extends State { child: const Text('Toggle generated colors').square(Dimensions.s), ).square(Dimensions.s), if (showGeneratedColors) - const Row(children: [ZetaText.displayMedium('Generated color swatches')]).squish(Dimensions.x8), + Row(children: [ZetaText.displayMedium('Generated color swatches')]).squish(Dimensions.x8), if (showGeneratedColors) ...generatedSwatches.entries.map( (value) => Row( - children: List.generate(10, (index) => 100 - (10 * index)) + children: List.generate(11, (index) => 110 - (10 * index)) .map( (e) => Expanded( child: Container( height: constraints.maxWidth / 10, - color: value.value[e], - child: FittedBox( - fit: BoxFit.scaleDown, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - DefaultTextStyle( - style: ZetaText.zetaBodyMedium - .copyWith(color: calculateTextColor(value.value[e] ?? Colors.white)), + color: e == 110 ? colors.surface : value.value[e], + child: e == 110 + ? SizedBox() + : FittedBox( + fit: BoxFit.scaleDown, child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Text('${value.key.toLowerCase().replaceAll(' ', '')}-$e'), - Text( - value.value[e].toString().replaceAll('Color(0xff', '#').substring(0, 7), + DefaultTextStyle( + style: ZetaText.zetaBodyMedium + .copyWith(color: calculateTextColor(value.value[e] ?? Colors.white)), + child: Column( + children: [ + Text('${value.key.toLowerCase().replaceAll(' ', '')}-$e'), + Text( + value.value[e] + .toString() + .replaceAll('Color(0xff', '#') + .substring(0, 7), + ), + ], + ), ), ], ), ), - ], - ), - ), ), ), ) diff --git a/example/lib/pages/typography_example.dart b/example/lib/pages/typography_example.dart index 1387457e..8be0e9b0 100644 --- a/example/lib/pages/typography_example.dart +++ b/example/lib/pages/typography_example.dart @@ -37,7 +37,7 @@ class TypographyExample extends StatelessWidget { }, ).toList(); - static const dedicatedSizes = [ + static final dedicatedSizes = [ ExampleModel( example: ZetaText.bodySmall(exampleText), wDescription: ZetaText.bodySmall(exampleParagraph, maxWidth: 66), @@ -170,13 +170,13 @@ class TypographyExample extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const ZetaText.headingLarge('Text').inline(Dimensions.x10), + ZetaText.headingLarge('Text').inline(Dimensions.x10), ...tokens.map(ExampleBuilder.new), const Divider().squish(Dimensions.x4), - const ZetaText.headingLarge('Universal sizes').inline(Dimensions.x10), + ZetaText.headingLarge('Universal sizes').inline(Dimensions.x10), ...universalSizes.map(ExampleBuilder.new), const Divider().squish(Dimensions.x4), - const ZetaText.headingLarge('Dedicated sizes').inline(Dimensions.x10), + ZetaText.headingLarge('Dedicated sizes').inline(Dimensions.x10), ...dedicatedSizes.map(ExampleBuilder.new), ], ), diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 92122f48..f632ebd4 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -16,7 +16,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index 60f1329d..d644e73f 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -203,7 +203,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index fb7259e1..83d88728 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ =3.0.1 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.7.0" diff --git a/example/test/color_test.dart b/example/test/color_test.dart index 6b4562e3..fb5009a5 100644 --- a/example/test/color_test.dart +++ b/example/test/color_test.dart @@ -3,8 +3,8 @@ import 'package:zeta_flutter/zeta_flutter.dart'; void main() { testWidgets('Dark mode value', (tester) async { - const ZetaColors light = ZetaColors(); - const ZetaColors dark = ZetaColors(isDarkMode: true); + final ZetaColors light = ZetaColors(); + final ZetaColors dark = ZetaColors(isDarkMode: true); expect(light.primary.shade10, dark.primary.shade100); expect(light.primary.shade20, dark.primary.shade90); @@ -14,8 +14,8 @@ void main() { }); testWidgets('AAA mode value', (tester) async { - const ZetaColors aa = ZetaColors(); - const ZetaColors aaa = ZetaColors(isAAA: true); + final ZetaColors aa = ZetaColors(); + final ZetaColors aaa = ZetaColors(isAAA: true); expect(aa.primary.primary, aa.primary.shade60); expect(aaa.primary.primary, aaa.primary.shade80); diff --git a/example/test/typography_test.dart b/example/test/typography_test.dart index c22ec2ca..c33abdf0 100644 --- a/example/test/typography_test.dart +++ b/example/test/typography_test.dart @@ -33,8 +33,8 @@ void main() { first: true, last: true, ), - const ZetaText(exampleText, style: ZetaText.zetaHeadingLarge, key: key3), - const ZetaText.headingLarge(exampleText, key: key4), + ZetaText(exampleText, style: ZetaText.zetaHeadingLarge, key: key3), + ZetaText.headingLarge(exampleText, key: key4), ], ); }, @@ -98,29 +98,4 @@ void main() { expect(padding2.padding, EdgeInsets.zero); expect(text3, text4); }); - - testWidgets('Responsive', (tester) async { - await tester.pumpWidget( - const TestWidget( - screenSize: Size(200, 400), - widget: Column(children: [ZetaText.displayLarge(exampleText, key: key1)]), - ), - ); - - final Finder zetaText1 = find.byKey(key1); - final InlineSpan text1 = - (find.descendant(of: zetaText1, matching: find.byType(RichText)).evaluate().first.widget as RichText).text; - - expect( - TextStyle( - fontSize: text1.style?.fontSize, - height: text1.style?.height, - fontWeight: text1.style?.fontWeight, - ), - ZetaText.zetaDisplayLargeResponsive, - ); - - expect(text1.style?.fontFamily, 'packages/zeta_flutter/IBMPlexSans'); - expect(text1.style?.color, const Color(0xFF1D1E23)); - }); } diff --git a/example/widgetbook/components/color_widgetbook.dart b/example/widgetbook/components/color_widgetbook.dart index e2a2c2f4..1c70872a 100644 --- a/example/widgetbook/components/color_widgetbook.dart +++ b/example/widgetbook/components/color_widgetbook.dart @@ -99,7 +99,7 @@ WidgetbookComponent colorWidgetBook() { MyRow(children: backdrop, title: 'Backdrop colors'), MyRow(children: primaries, title: 'Primary colors'), MyRow(children: alerts, title: 'Alert colors'), - const Row(children: [ZetaText.displayMedium('Full color swatches')]).squish(Dimensions.x8), + Row(children: [ZetaText.displayMedium('Full color swatches')]).squish(Dimensions.x8), ...swatches.entries.map( (value) { return Row( diff --git a/example/widgetbook/components/typography_widgetbook.dart b/example/widgetbook/components/typography_widgetbook.dart index d7f6d879..0d638ff6 100644 --- a/example/widgetbook/components/typography_widgetbook.dart +++ b/example/widgetbook/components/typography_widgetbook.dart @@ -8,7 +8,6 @@ WidgetbookComponent textWidgetBook() { 'Body small': ZetaText.zetaBodySmall, 'Body medium': ZetaText.zetaBodyMedium, 'Body large': ZetaText.zetaBodyLarge, - 'Description': ZetaText.zetaDescription, 'Headline small': ZetaText.zetaHeadingSmall, 'Headline medium': ZetaText.zetaHeadingMedium, 'Headline large': ZetaText.zetaHeadingLarge, diff --git a/lib/assets/fonts/IBMPlexSans-Light.otf b/lib/src/assets/fonts/IBMPlexSans-Light.otf similarity index 100% rename from lib/assets/fonts/IBMPlexSans-Light.otf rename to lib/src/assets/fonts/IBMPlexSans-Light.otf diff --git a/lib/assets/fonts/IBMPlexSans-Medium.otf b/lib/src/assets/fonts/IBMPlexSans-Medium.otf similarity index 100% rename from lib/assets/fonts/IBMPlexSans-Medium.otf rename to lib/src/assets/fonts/IBMPlexSans-Medium.otf diff --git a/lib/assets/fonts/IBMPlexSans-Regular.otf b/lib/src/assets/fonts/IBMPlexSans-Regular.otf similarity index 100% rename from lib/assets/fonts/IBMPlexSans-Regular.otf rename to lib/src/assets/fonts/IBMPlexSans-Regular.otf diff --git a/lib/components/grid.dart b/lib/src/components/grid.dart similarity index 92% rename from lib/components/grid.dart rename to lib/src/components/grid.dart index 0da36dd8..ddac4c9f 100644 --- a/lib/components/grid.dart +++ b/lib/src/components/grid.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../theme/breakpoints.dart'; @@ -142,4 +143,13 @@ class ZetaGrid extends StatelessWidget { }, ); } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('hybrid', hybrid)); + properties.add(DoubleProperty('col', col)); + properties.add(DiagnosticsProperty('noGaps', noGaps)); + properties.add(IntProperty('asymmetricWeight', asymmetricWeight)); + } } diff --git a/lib/components/spacing.dart b/lib/src/components/spacing.dart similarity index 95% rename from lib/components/spacing.dart rename to lib/src/components/spacing.dart index 99daf6e9..9762c6fc 100644 --- a/lib/components/spacing.dart +++ b/lib/src/components/spacing.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import '../tokens.dart'; @@ -132,6 +133,13 @@ class ZetaSpacing extends StatelessWidget { return child.inlineEnd(size); } } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(EnumProperty('type', type)); + properties.add(DoubleProperty('size', size)); + } } /// Extension to get Spacing type as [EdgeInsets] from a double. diff --git a/lib/components/text.dart b/lib/src/components/text.dart similarity index 68% rename from lib/components/text.dart rename to lib/src/components/text.dart index df1d1031..9b49f99f 100644 --- a/lib/components/text.dart +++ b/lib/src/components/text.dart @@ -1,35 +1,8 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import '../theme/theme.dart'; +import '../../zeta_flutter.dart'; import '../tokens.dart' as tokens; -import 'spacing.dart'; - -/// Determine whether to show mobile text for heading / display. -extension ResponsiveBreak on DeviceType { - /// Determine whether to show mobile text for heading / display. - bool get responsiveText { - return name == 'mobileLandscape' || name == 'mobilePortrait'; - } -} - -/// Extension to add [mobileStyle] to [TextStyle]. -extension Responsive on TextStyle { - /// Gets a responsive variant of a Zeta TextStyle. - /// - /// Variants exist for Zeta Heading and Display TextStyles, otherwise the TextStyle is returned unchanged. - TextStyle get mobileStyle { - final Map responsiveMap = { - ZetaText.zetaHeadingSmall: ZetaText.zetaHeadingSmallResponsive, - ZetaText.zetaHeadingMedium: ZetaText.zetaHeadingMediumResponsive, - ZetaText.zetaHeadingLarge: ZetaText.zetaHeadingLargeResponsive, - ZetaText.zetaDisplaySmall: ZetaText.zetaDisplaySmallResponsive, - ZetaText.zetaDisplayMedium: ZetaText.zetaDisplayMediumResponsive, - ZetaText.zetaDisplayLarge: ZetaText.zetaDisplayLargeResponsive, - }; - - return responsiveMap[this] ?? this; - } -} /// {@template zeta-component-text} /// ZetaText component. @@ -45,7 +18,7 @@ class ZetaText extends StatelessWidget { /// Text styles for Zeta. /// /// {@macro zeta-theme} - static const TextTheme textTheme = TextTheme( + static TextTheme textTheme = TextTheme( displayLarge: zetaDisplayLarge, displayMedium: zetaDisplayMedium, displaySmall: zetaDisplaySmall, @@ -63,7 +36,7 @@ class ZetaText extends StatelessWidget { labelSmall: zetaLabelSmall, ); - /// Buids text theme for app based on an instance of [ZetaColors]. + /// Builds text theme for app based on an instance of [ZetaColors]. static TextTheme textThemeBuilder(ZetaColors colors) { return TextTheme( displayLarge: zetaDisplayLarge.copyWith(color: colors.textDefault), @@ -84,41 +57,30 @@ class ZetaText extends StatelessWidget { ); } - /// Returns responsive text style. + /// {@template zeta-type-body-xs} + /// Smallest body text. /// - /// Should only be used on [DeviceType.mobileLandscape] and [DeviceType.mobilePortrait]. + /// Used for UI components and UI content design. /// - /// Automatically applied in [ZetaText] but must be applied manually otherwise. - static const TextTheme textThemeMobile = TextTheme( - displayLarge: zetaDisplayLargeResponsive, - displayMedium: zetaDisplayMediumResponsive, - displaySmall: zetaDisplaySmallResponsive, - headlineLarge: zetaHeadingLargeResponsive, - headlineMedium: zetaHeadingMediumResponsive, - headlineSmall: zetaHeadingSmallResponsive, - titleLarge: zetaTitleLarge, - titleMedium: zetaTitleMedium, - titleSmall: zetaTitleSmall, - bodyLarge: zetaBodyLarge, - bodyMedium: zetaBodyMedium, - bodySmall: zetaBodySmall, - labelLarge: zetaLabelLarge, - labelMedium: zetaLabelMedium, - labelSmall: zetaLabelSmall, + /// {@endtemplate} + static TextStyle zetaBodyXSmall = const TextStyle( + fontSize: tokens.Dimensions.x3, + fontWeight: FontWeight.w400, + height: tokens.Dimensions.x3 / tokens.Dimensions.x4, ); /// {@template zeta-type-body-s} - /// Smallest body text. + /// Small body text. /// /// Used for UI components and UI content design. /// /// See also: /// * [TextTheme.bodySmall]. /// {@endtemplate} - static const TextStyle zetaBodySmall = TextStyle( - fontSize: tokens.Dimensions.x3, + static TextStyle zetaBodySmall = const TextStyle( + fontSize: tokens.Dimensions.x3_5, fontWeight: FontWeight.w400, - height: tokens.Dimensions.x4 / tokens.Dimensions.x3, + height: 18 / 14, ); /// {@template zeta-type-body-m} @@ -129,7 +91,7 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.bodyMedium]. /// {@endtemplate} - static const TextStyle zetaBodyMedium = TextStyle( + static TextStyle zetaBodyMedium = const TextStyle( fontSize: tokens.Dimensions.x4, fontWeight: FontWeight.w400, height: tokens.Dimensions.x6 / tokens.Dimensions.x4, @@ -143,10 +105,10 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.bodyLarge]. /// {@endtemplate} - static const TextStyle zetaBodyLarge = TextStyle( + static TextStyle zetaBodyLarge = const TextStyle( fontSize: tokens.Dimensions.x5, fontWeight: FontWeight.w400, - height: tokens.Dimensions.x7 / tokens.Dimensions.x5, + height: tokens.Dimensions.x6 / tokens.Dimensions.x5, ); /// {@template zeta-type-label-s} @@ -157,10 +119,10 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.labelSmall]. /// {@endtemplate} - static const TextStyle zetaLabelSmall = TextStyle( + static TextStyle zetaLabelSmall = const TextStyle( fontSize: tokens.Dimensions.x3, fontWeight: FontWeight.w500, - height: 1, + height: tokens.Dimensions.x4 / tokens.Dimensions.x3, ); /// {@template zeta-type-label-m} @@ -171,10 +133,10 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.labelMedium]. /// {@endtemplate} - static const TextStyle zetaLabelMedium = TextStyle( - fontSize: tokens.Dimensions.x4, + static TextStyle zetaLabelMedium = const TextStyle( + fontSize: tokens.Dimensions.x3_5, fontWeight: FontWeight.w500, - height: 1, + height: tokens.Dimensions.x3_5 / tokens.Dimensions.x5, ); /// {@template zeta-type-label-l} @@ -185,52 +147,56 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.labelLarge]. /// {@endtemplate} - static const TextStyle zetaLabelLarge = TextStyle( - fontSize: tokens.Dimensions.x5, + static TextStyle zetaLabelLarge = const TextStyle( + fontSize: tokens.Dimensions.x4, fontWeight: FontWeight.w500, - height: 1, + height: tokens.Dimensions.x4 / tokens.Dimensions.x6, ); /// {@template zeta-type-title-s} - /// Small title text. + /// Heading 6 / Small title text. /// /// Used for UI components and UI content design. /// /// See also: /// * [TextTheme.titleSmall]. /// {@endtemplate} - static const TextStyle zetaTitleSmall = TextStyle( + static TextStyle zetaTitleSmall = const TextStyle( fontSize: tokens.Dimensions.x3, fontWeight: FontWeight.w500, height: tokens.Dimensions.x4 / tokens.Dimensions.x3, ); /// {@template zeta-type-title-m} - /// Medium title text. + /// Heading 5 / Medium title text. /// /// Used for UI components and UI content design. /// /// See also: /// * [TextTheme.titleMedium]. /// {@endtemplate} - static const TextStyle zetaTitleMedium = TextStyle( + static TextStyle zetaTitleMedium = const TextStyle( fontSize: tokens.Dimensions.x4, fontWeight: FontWeight.w500, height: tokens.Dimensions.x5 / tokens.Dimensions.x4, ); +// TODO(tokens): How to add color and font family here? +// Both can be changed at runtime so can;t be const. +// But also how do we access them without state? + /// {@template zeta-type-title-l} - /// Large title text. + /// Heading 4 / Large title text. /// /// Used for UI sections and landing pages. /// /// See also: /// * [TextTheme.titleLarge]. /// {@endtemplate} - static const TextStyle zetaTitleLarge = TextStyle( + static TextStyle zetaTitleLarge = const TextStyle( fontSize: tokens.Dimensions.x5, fontWeight: FontWeight.w500, - height: tokens.Dimensions.x5 / tokens.Dimensions.x6, + height: tokens.Dimensions.x4 / tokens.Dimensions.x5, ); /// {@template zeta-type-heading-s} @@ -241,7 +207,7 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.headlineSmall]. /// {@endtemplate} - static const TextStyle zetaHeadingSmall = TextStyle( + static TextStyle zetaHeadingSmall = const TextStyle( fontSize: tokens.Dimensions.x6, fontWeight: FontWeight.w500, height: tokens.Dimensions.x7 / tokens.Dimensions.x6, @@ -255,7 +221,7 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.headlineMedium]. /// {@endtemplate} - static const TextStyle zetaHeadingMedium = TextStyle( + static TextStyle zetaHeadingMedium = const TextStyle( fontSize: tokens.Dimensions.x7, fontWeight: FontWeight.w500, height: tokens.Dimensions.x8 / tokens.Dimensions.x7, @@ -268,7 +234,7 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.headlineLarge]. /// {@endtemplate} - static const TextStyle zetaHeadingLarge = TextStyle( + static TextStyle zetaHeadingLarge = const TextStyle( fontSize: tokens.Dimensions.x8, fontWeight: FontWeight.w500, height: tokens.Dimensions.x9 / tokens.Dimensions.x8, @@ -282,11 +248,7 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.displaySmall]. /// {@endtemplate} - static const TextStyle zetaDisplaySmall = TextStyle( - fontSize: tokens.Dimensions.x9, - fontWeight: FontWeight.w300, - height: tokens.Dimensions.x9 / tokens.Dimensions.x10, - ); + static TextStyle zetaDisplaySmall = zetaHeadingSmall; /// {@template zeta-type-display-m} /// Medium display text. @@ -296,11 +258,7 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.displayMedium]. /// {@endtemplate} - static const TextStyle zetaDisplayMedium = TextStyle( - fontSize: tokens.Dimensions.x11, - fontWeight: FontWeight.w300, - height: tokens.Dimensions.x12 / tokens.Dimensions.x11, - ); + static TextStyle zetaDisplayMedium = zetaHeadingMedium; /// {@template zeta-type-display-l} /// Large display text. @@ -309,88 +267,16 @@ class ZetaText extends StatelessWidget { /// See also: /// * [TextTheme.displayLarge]. /// {@endtemplate} - static const TextStyle zetaDisplayLarge = TextStyle( - fontSize: tokens.Dimensions.x13, - fontWeight: FontWeight.w300, - height: tokens.Dimensions.x14 / tokens.Dimensions.x13, - ); - - /// {@template zeta-type-description} - /// Description text. - /// - /// This size falls off the mini unit scale, to meet very specific criteria, and forced to be used for single line text only. It never changes line height even in component design. - /// - /// Used for UI components and UI content design. - /// {@endtemplate} - static const TextStyle zetaDescription = TextStyle( - fontSize: tokens.Dimensions.x3_5, - fontWeight: FontWeight.w500, - height: tokens.Dimensions.x4 / tokens.Dimensions.x3_5, - ); - - /// {@macro zeta-type-responsive} - /// - /// {@macro zeta-type-heading-s} - static const TextStyle zetaHeadingSmallResponsive = TextStyle( - fontSize: tokens.Dimensions.x5, - fontWeight: FontWeight.w500, - height: tokens.Dimensions.x6 / tokens.Dimensions.x5, - ); - - /// {@macro zeta-type-responsive} - /// - /// {@macro zeta-type-heading-m} - static const TextStyle zetaHeadingMediumResponsive = TextStyle( - fontSize: tokens.Dimensions.x6, - fontWeight: FontWeight.w500, - height: tokens.Dimensions.x7 / tokens.Dimensions.x6, - ); - - /// {@macro zeta-type-responsive} - /// - /// {@macro zeta-type-heading-l} - static const TextStyle zetaHeadingLargeResponsive = TextStyle( - fontSize: tokens.Dimensions.x7, - fontWeight: FontWeight.w500, - height: tokens.Dimensions.x8 / tokens.Dimensions.x7, - ); - - /// {@macro zeta-type-responsive} - /// - /// {@macro zeta-type-display-s} - static const TextStyle zetaDisplaySmallResponsive = TextStyle( - fontSize: tokens.Dimensions.x8, - fontWeight: FontWeight.w300, - height: tokens.Dimensions.x9 / tokens.Dimensions.x8, - ); - - /// {@macro zeta-type-responsive} - /// - /// {@macro zeta-type-display-m} - static const TextStyle zetaDisplayMediumResponsive = TextStyle( - fontSize: tokens.Dimensions.x9, - fontWeight: FontWeight.w300, - height: tokens.Dimensions.x10 / tokens.Dimensions.x9, - ); - - /// {@template zeta-type-responsive} - /// Responsive text style, used for [DeviceType.mobileLandscape] and [DeviceType.mobilePortrait]. - /// {@endtemplate} - /// - /// {@macro zeta-type-display-l} - static const TextStyle zetaDisplayLargeResponsive = TextStyle( - fontSize: tokens.Dimensions.x11, - fontWeight: FontWeight.w300, - height: tokens.Dimensions.x12 / tokens.Dimensions.x11, - ); + static TextStyle zetaDisplayLarge = zetaHeadingLarge; /// Gets approximate char width based on width of O in IBM Plex Sans /// /// Only works for IBM Plex. - static double ch({double multiplier = _defaultChMultiplier, TextStyle style = zetaBodyMedium}) { + static double _ch({double multiplier = _defaultChMultiplier, TextStyle? style}) { + final setStyle = style ?? zetaBodyMedium; const plexCh = 0.6; - return multiplier * plexCh * (style.fontSize ?? tokens.Dimensions.x3); + return multiplier * plexCh * (setStyle.fontSize ?? tokens.Dimensions.x3); } /// The text to be displayed. @@ -405,7 +291,7 @@ class ZetaText extends StatelessWidget { /// /// See also: /// * [Text.style]. - final TextStyle style; + final TextStyle? style; /// Sets text color. /// @@ -413,9 +299,9 @@ class ZetaText extends StatelessWidget { /// * [TextStyle.color]. final Color? textColor; - /// Max width of Text box using [ch]. Not measured in dp / px. + /// Max width of Text box using [_ch]. Not measured in dp / px. /// - /// [ch] approximates width of a character using O as basis, so a maxWidth of 60 theoretically returns a max width containing 60 characters. + /// [_ch] approximates width of a character using O as basis, so a maxWidth of 60 theoretically returns a max width containing 60 characters. /// /// Only works with 'IBM Plex Sans'. final double? maxWidth; @@ -483,7 +369,7 @@ class ZetaText extends StatelessWidget { /// Constructor for [ZetaText]. const ZetaText( this.data, { - this.style = ZetaText.zetaBodyMedium, + this.style, this.resetHeight = false, this.textColor, this.fontSize, @@ -518,48 +404,66 @@ class ZetaText extends StatelessWidget { @override Widget build(BuildContext context) { - const TextStyle tempTextStyle = ZetaText.zetaBodyMedium; - final TextStyle thisStyle = tempTextStyle.copyWith( - fontSize: style.fontSize, - fontWeight: style.fontWeight, - height: style.height, + TextStyle thisStyle = (style ?? ZetaText.zetaBodyMedium).copyWith( + fontSize: style?.fontSize, + fontWeight: style?.fontWeight, + height: style?.height, ); String data = this.data ?? ''; final Color color = textColor ?? ZetaColors.of(context).textDefault; - TextStyle textStyle = context.deviceType.responsiveText ? thisStyle.mobileStyle : thisStyle; - textStyle = textStyle.copyWith( - fontSize: (fontSize ?? textStyle.fontSize ?? tokens.Typography.defaultTextSize) * + thisStyle = thisStyle.copyWith( + fontSize: (fontSize ?? thisStyle.fontSize ?? tokens.Typography.defaultTextSize) * MediaQuery.of(context).textScaleFactor, height: _fontSize, fontWeight: fontWeight, decoration: decoration ?? TextDecoration.none, fontStyle: fontStyle, color: color, + fontFamily: Theme.of(context).fontFamily, ); - if (resetHeight) textStyle = textStyle.copyWith(height: style == ZetaText.zetaDescription ? null : 1); + if (resetHeight) thisStyle = thisStyle.copyWith(height: 1); if (upperCase) data = data.toUpperCase(); return Padding( padding: _padding, child: maxWidth == null - ? Text(data, style: textStyle, textDirection: textDirection) + ? Text(data, style: thisStyle, textDirection: textDirection) : Align( alignment: textDirection == TextDirection.rtl ? Alignment.centerRight : Alignment.centerLeft, child: SizedBox( - width: maxWidth == null ? null : ch(multiplier: maxWidth ?? 0, style: textStyle), - child: Text(data, style: textStyle, textDirection: textDirection), + width: maxWidth == null ? null : _ch(multiplier: maxWidth ?? 0, style: thisStyle), + child: Text(data, style: thisStyle, textDirection: textDirection), ), ), ); } + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-body-xs} + ZetaText.bodyXSmall( + this.data, { + this.resetHeight = false, + this.maxWidth, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + super.key, + }) : style = zetaBodyXSmall; + /// {@macro zeta-component-text} /// /// {@macro zeta-type-body-s} - const ZetaText.bodySmall( + ZetaText.bodySmall( this.data, { this.resetHeight = false, this.maxWidth, @@ -578,7 +482,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-body-m} - const ZetaText.bodyMedium( + ZetaText.bodyMedium( this.data, { this.resetHeight = false, this.decoration, @@ -597,7 +501,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-body-l} - const ZetaText.bodyLarge( + ZetaText.bodyLarge( this.data, { this.resetHeight = false, this.decoration, @@ -616,7 +520,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-label-s} - const ZetaText.labelSmall( + ZetaText.labelSmall( this.data, { this.resetHeight = false, this.decoration, @@ -635,7 +539,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-label-m} - const ZetaText.labelMedium( + ZetaText.labelMedium( this.data, { this.resetHeight = false, this.decoration, @@ -654,7 +558,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-label-l} - const ZetaText.labelLarge( + ZetaText.labelLarge( this.data, { this.resetHeight = false, this.decoration, @@ -673,7 +577,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-title-s} - const ZetaText.titleSmall( + ZetaText.titleSmall( this.data, { this.resetHeight = false, this.decoration, @@ -692,7 +596,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-title-m} - const ZetaText.titleMedium( + ZetaText.titleMedium( this.data, { this.resetHeight = false, this.decoration, @@ -711,7 +615,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-title-l} - const ZetaText.titleLarge( + ZetaText.titleLarge( this.data, { this.resetHeight = false, this.decoration, @@ -730,7 +634,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-heading-s} - const ZetaText.headingSmall( + ZetaText.headingSmall( this.data, { this.resetHeight = false, this.decoration, @@ -749,7 +653,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-heading-m} - const ZetaText.headingMedium( + ZetaText.headingMedium( this.data, { this.decoration, this.fontSize, @@ -768,7 +672,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-heading-l} - const ZetaText.headingLarge( + ZetaText.headingLarge( this.data, { this.resetHeight = false, this.decoration, @@ -787,7 +691,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-display-s} - const ZetaText.displaySmall( + ZetaText.displaySmall( this.data, { this.resetHeight = false, this.decoration, @@ -806,7 +710,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-display-m} - const ZetaText.displayMedium( + ZetaText.displayMedium( this.data, { this.resetHeight = false, this.decoration, @@ -825,7 +729,7 @@ class ZetaText extends StatelessWidget { /// {@macro zeta-component-text} /// /// {@macro zeta-type-display-l} - const ZetaText.displayLarge( + ZetaText.displayLarge( this.data, { this.decoration, this.fontSize, @@ -840,23 +744,29 @@ class ZetaText extends StatelessWidget { this.maxWidth, super.key, }) : style = zetaDisplayLarge; + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(StringProperty('data', data)); + properties.add(DiagnosticsProperty('style', style)); + properties.add(ColorProperty('textColor', textColor)); + properties.add(DoubleProperty('maxWidth', maxWidth)); + properties.add(DoubleProperty('fontSize', fontSize)); + properties.add(DiagnosticsProperty('fontWeight', fontWeight)); + properties.add(EnumProperty('fontStyle', fontStyle)); + properties.add(DiagnosticsProperty('upperCase', upperCase)); + properties.add(DiagnosticsProperty('decoration', decoration)); + properties.add(EnumProperty('textDirection', textDirection)); + properties.add(DiagnosticsProperty('first', first)); + properties.add(DiagnosticsProperty('last', last)); + properties.add(DiagnosticsProperty('resetHeight', resetHeight)); + } +} - /// {@macro zeta-component-text} - /// - /// {@macro zeta-type-description} - const ZetaText.description( - this.data, { - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.resetHeight = false, - this.maxWidth, - super.key, - }) : style = zetaDescription; +/// Extension to add Zeta's extra small text size. +extension XSmall on TextTheme { + /// Smallest body text size. + TextStyle? get bodyXSmall { + return ZetaText.zetaBodyXSmall; + } } diff --git a/lib/theme/breakpoints.dart b/lib/src/theme/breakpoints.dart similarity index 100% rename from lib/theme/breakpoints.dart rename to lib/src/theme/breakpoints.dart diff --git a/lib/theme/colors.dart b/lib/src/theme/colors.dart similarity index 91% rename from lib/theme/colors.dart rename to lib/src/theme/colors.dart index c96b689e..198d8a22 100644 --- a/lib/theme/colors.dart +++ b/lib/src/theme/colors.dart @@ -2,7 +2,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import '../../zeta_flutter.dart'; +import '../../../zeta_flutter.dart'; /// Zeta Colors. /// @@ -43,45 +43,59 @@ class ZetaColors { final ZetaColorSwatch _teal; final ZetaColorSwatch _pink; + final Color? _background; + final Color? _surface; + final Color? _white; + final Color? _black; + final Color? _onPrimary; final Color? _onSecondary; final Color? _onNegative; - final Color? _background; final Color? _onBackground; - final Color? _surface; final Color? _onSurface; - final Color? _white; - final Color? _black; + + final Map _generatedColors; static const _minShade = 10; - static const Color _defaultShadow = Color(0x1A49505E); - /// Generated color for use on [secondary]. + /// Generated color for use on [primary]. /// /// {@template zeta-color-on-X} /// If [secondary] is dark, [onSecondary] will be [textDarkMode]. /// /// If [secondary] is light, [onSecondary] will be [textLightMode]. /// {@endtemplate} - Color get onSecondary => - _onSecondary ?? - computeForeground( - input: secondary, - light: textDarkMode, - dark: textLightMode, - ); + Color get onPrimary { + if (_onPrimary != null) return _onPrimary!; + // if (_generatedColors['onPrimary'] != null) return _generatedColors['onPrimary']!; + _generatedColors['onPrimary'] = computeForegroundFromTheme(input: primary); + + return _generatedColors['onPrimary']!; + } /// Generated color for use on [secondary]. /// /// {@macro zeta-color-on-X} - Color get onPrimary => _onPrimary ?? computeForeground(input: primary, light: textDarkMode, dark: textLightMode); + Color get onSecondary { + if (_onSecondary != null) return _onSecondary!; + if (_generatedColors['onSecondary'] != null) return _generatedColors['onSecondary']!; + _generatedColors['onSecondary'] = computeForegroundFromTheme(input: secondary); + + return _generatedColors['onSecondary']!; + } /// Generated color for use on [negative]. /// /// {@macro zeta-color-on-X} - Color get onNegative => _onNegative ?? computeForeground(input: negative, light: textDarkMode, dark: textLightMode); + Color get onNegative { + if (_onNegative != null) return _onNegative!; + if (_generatedColors['onNegative'] != null) return _generatedColors['onNegative']!; + _generatedColors['onNegative'] = computeForegroundFromTheme(input: negative); + + return _generatedColors['onNegative']!; + } - /// Generated color for use on [surface]. + /// On surface color. /// /// {@macro zeta-color-on-X} Color get onSurface => _onSurface ?? textDefault; @@ -165,7 +179,7 @@ class ZetaColors { /// Background color. /// /// See [ColorScheme.background]. - Color get background => _background ?? surfacePrimary; + Color get background => _background ?? warm.shade10; /// On background color. /// @@ -195,7 +209,7 @@ class ZetaColors { /// /// * Light mode: `ZetaColors.cool.10`. /// * Dark mode: `ZetaColors.warm.30`. - Color get surfaceSecondary => isDarkMode ? warm.shade10 : cool.shade10; + Color get surfaceSecondary => isDarkMode ? cool.shade10 : white; /// Tertiary surface color. /// @@ -222,7 +236,7 @@ class ZetaColors { /// {@macro zeta-color-dark} /// /// {@macro zeta-color-aaa} - Color get positive => green.primary; + Color get positive => green.shade60; /// Red negative color. /// @@ -233,7 +247,7 @@ class ZetaColors { /// {@macro zeta-color-dark} /// /// {@macro zeta-color-aaa} - Color get negative => red.primary; + Color get negative => red.shade60; /// Orange warning color. /// @@ -425,7 +439,7 @@ class ZetaColors { List get rainbow => [red, orange, yellow, green, blue, teal, pink]; /// Default constructor for instance of [ZetaColors]. - const ZetaColors({ + ZetaColors({ ZetaColorSwatch primary = ZetaColorBase.blue, ZetaColorSwatch secondary = ZetaColorBase.blue, ZetaColorSwatch cool = ZetaColorBase.greyCool, @@ -442,7 +456,7 @@ class ZetaColors { Color linkVisitedLight = ZetaColorBase.linkVisitedLight, Color linkDark = ZetaColorBase.linkDark, Color linkVisitedDark = ZetaColorBase.linkVisitedDark, - this.shadow = _defaultShadow, + this.shadow = ZetaColorBase.shadow, this.isDarkMode = false, this.isAAA = false, Color? onPrimary, @@ -478,7 +492,8 @@ class ZetaColors { _surface = surface, _onSurface = onSurface, _black = black, - _white = white; + _white = white, + _generatedColors = {}; /// Creates a [ZetaColors] from individual colors and generates their full swatches with [ZetaColorSwatch.fromColor] ZetaColors.fromColors({ @@ -507,7 +522,7 @@ class ZetaColors { Color? onSurface, Color? black, Color? white, - this.shadow = _defaultShadow, + this.shadow = ZetaColorBase.shadow, this.isDarkMode = false, this.isAAA = false, }) : _primary = primary?.zetaColorSwatch ?? ZetaColorBase.blue, @@ -534,7 +549,8 @@ class ZetaColors { _surface = surface, _onSurface = onSurface, _black = black, - _white = white; + _white = white, + _generatedColors = {}; /// Returns a new ZetaColors instance, with the copied fields applied. ZetaColors copyWith({ @@ -585,7 +601,10 @@ class ZetaColors { Color light = ZetaColorBase.white, Color dark = ZetaColorBase.black, }) => - input.computeLuminance() > 0.5 ? dark : light; + input.isLight ? dark : light; + + /// Calculates foreground color based on luminance of input. + Color computeForegroundFromTheme({required Color input}) => input.isLight ? textDefault : textInverse; /// Setter for updating ZetaColors instance based on current context. static void setColors(BuildContext context, ZetaColors colors) { @@ -595,6 +614,14 @@ class ZetaColors { } } + /// Setter for updating if dark mode is activated for ZetaColors instance based on current context. + static void setDarkMode(BuildContext context, bool val) { + final ZetaState? state = context.findAncestorStateOfType(); + if (state != null) { + state.colors = state.colors.copyWith(isDarkMode: val); + } + } + /// Returns instance of [ZetaColors] from the current context. /// /// For this function to work properly, it is required that the current context is beneath a [ZetaColors] widget @@ -602,7 +629,8 @@ class ZetaColors { static ZetaColors of(BuildContext context) { final ZetaState? state = context.findAncestorStateOfType(); - return state?.colors ?? const ZetaColors(); + // ignore: prefer_const_constructors + return state?.colors ?? ZetaColors(); } } @@ -741,6 +769,9 @@ extension ColorExtension on Color { return hslColor.withLightness(percentage).toColor(); } + /// Uses [computeLuminance] to determine if a color if light. + bool get isLight => computeLuminance() > 0.5; + /// Gets lightness of color. double get lightness => HSLColor.fromColor(this).lightness; diff --git a/lib/theme/colors_base.dart b/lib/src/theme/colors_base.dart similarity index 97% rename from lib/theme/colors_base.dart rename to lib/src/theme/colors_base.dart index da47e689..36d2a5e6 100644 --- a/lib/theme/colors_base.dart +++ b/lib/src/theme/colors_base.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../zeta_flutter.dart'; +import '../../../zeta_flutter.dart'; /// Default set of Zeta Colors that can be used to make a [ZetaColors] instance. /// @@ -206,4 +206,7 @@ class ZetaColorBase { /// Visited link color for dark mode. static const Color linkVisitedDark = Color(0xFF47A3FF); + + /// Default shadow color. + static const Color shadow = Color(0x1A49505E); } diff --git a/lib/theme/constants.dart b/lib/src/theme/constants.dart similarity index 100% rename from lib/theme/constants.dart rename to lib/src/theme/constants.dart diff --git a/lib/theme/theme.dart b/lib/src/theme/theme.dart similarity index 79% rename from lib/theme/theme.dart rename to lib/src/theme/theme.dart index 9dfafb28..aed90f93 100644 --- a/lib/theme/theme.dart +++ b/lib/src/theme/theme.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../zeta_flutter.dart'; +import '../../../zeta_flutter.dart'; export 'breakpoints.dart'; export 'colors.dart'; @@ -34,8 +34,7 @@ class ZetaTheme { /// {@endtemplate} static ThemeData zetaLight({ZetaThemeData? initialTheme}) { return ThemeData( - colorScheme: - initialTheme?.colorScheme ?? initialTheme?.zetaColors?.toColorScheme ?? const ZetaColors().toColorScheme, + colorScheme: initialTheme?.colorScheme ?? initialTheme?.zetaColors?.toColorScheme ?? ZetaColors().toColorScheme, fontFamily: initialTheme?.fontFamily ?? 'packages/zeta_flutter/IBMPlexSans', // TODO(tokens): reinstate tokens.Typography.fontFamily when we have a plan for tokens textTheme: initialTheme?.textTheme ?? ZetaText.textTheme, @@ -49,7 +48,7 @@ class ZetaTheme { return ThemeData( colorScheme: initialTheme?.colorScheme ?? initialTheme?.zetaColors?.copyWith(isDarkMode: true).toColorScheme ?? - const ZetaColors().toColorScheme, + ZetaColors().toColorScheme, fontFamily: initialTheme?.fontFamily ?? 'packages/zeta_flutter/IBMPlexSans', textTheme: initialTheme?.textTheme ?? ZetaText.textTheme, ); @@ -61,7 +60,7 @@ class ZetaTheme { /// /// {@macro zeta-theme} static ThemeData builder({ZetaThemeData? initialTheme}) { - final ZetaColors colors = initialTheme?.zetaColors ?? const ZetaColors(); + final ZetaColors colors = initialTheme?.zetaColors ?? ZetaColors(); return ThemeData( colorScheme: initialTheme?.colorScheme ?? colors.toColorScheme, @@ -114,3 +113,16 @@ class ZetaThemeData { ); } } + +/// Font family for whole theme. +extension FontFamily on ThemeData { + /// Default font family used by theme. + /// + /// We assume that the same font should be used for all text styles in a theme, therefore we can extract from any child theme. + String? get fontFamily => textTheme.bodyMedium?.fontFamily; + + /// Default text color used by theme. + /// + /// We assume that the same font should be used for all text styles in a theme, therefore we can extract from any child theme. + Color? get defaultColor => textTheme.bodyMedium?.color; +} diff --git a/lib/tokens.dart b/lib/src/tokens.dart similarity index 99% rename from lib/tokens.dart rename to lib/src/tokens.dart index 4d44d476..00ad26c4 100644 --- a/lib/tokens.dart +++ b/lib/src/tokens.dart @@ -1,4 +1,4 @@ -import '../zeta_flutter.dart'; +import '../../zeta_flutter.dart'; /// Tokens that are used for [ZetaSpacing]. /// diff --git a/lib/utils/extensions.dart b/lib/src/utils/extensions.dart similarity index 100% rename from lib/utils/extensions.dart rename to lib/src/utils/extensions.dart diff --git a/lib/src/zeta.dart b/lib/src/zeta.dart new file mode 100644 index 00000000..085224d5 --- /dev/null +++ b/lib/src/zeta.dart @@ -0,0 +1,116 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import '../../zeta_flutter.dart'; + +/// Wrapper widget for apps using zeta. +/// +/// Please place this widget at the top level of the application. +/// Typically this would be wrapping a [MaterialApp], [WidgetsApp] or [CupertinoApp]. +/// +/// Without this widget in the tree, theming and colors may not work. +class Zeta extends StatefulWidget { + /// Base theme for the app. Zeta styles will be applied on top of this. + final ZetaThemeData? theme; + + /// Override for custom colors. + final ZetaColors? colors; + + /// Builder for the app. + /// + /// Returns theme and colors. + final Widget Function(BuildContext, ThemeData, ZetaColors) builder; + + /// Constructor for [Zeta]. + const Zeta({required this.builder, this.theme, this.colors, super.key}); + @override + State createState() => ZetaState(); + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('theme', theme)); + properties.add(DiagnosticsProperty('colors', colors)); + properties + .add(ObjectFlagProperty.has('builder', builder)); + } +} + +/// State for [Zeta]. +class ZetaState extends State { + ZetaColors _colors = ZetaColors(); + late ThemeData _theme; + late ZetaThemeData _zetaTheme; + + /// Theme used by zeta. + ZetaThemeData get zetaTheme => _zetaTheme; + set zetaTheme(ZetaThemeData value) { + _zetaTheme = value; + setState(() { + _theme = ZetaTheme.builder(initialTheme: zetaTheme); + }); + } + + bool _ready = false; + + /// Colors for app. + /// + /// Access using `ZetaColors.of(context)`. + ZetaColors get colors => _colors; + + /// Sets colors for app. + /// + /// Access using `ZetaColors.setColors(context, colors)`. + /// + /// When called, resets top-level key to force UI rebuild of app. + set colors(ZetaColors value) { + if (_colors != value) { + setState(() { + _colors = value; + _theme = ZetaTheme.builder(initialTheme: zetaTheme.copyWith(zetaColors: value)); + }); + } + } + + @override + void initState() { + super.initState(); + + zetaTheme = (widget.theme ?? const ZetaThemeData()).copyWith(zetaColors: colors); + colors = widget.colors ?? ZetaColors(); + _ready = true; + } + + @override + void didUpdateWidget(Zeta oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.colors != widget.colors) { + colors = widget.colors ?? ZetaColors(); + zetaTheme = (widget.theme ?? const ZetaThemeData()).copyWith(zetaColors: colors); + } + if (oldWidget.theme != widget.theme) { + zetaTheme = (widget.theme ?? const ZetaThemeData()).copyWith(zetaColors: colors); + } + } + + @override + Widget build(BuildContext context) { + if (_ready) { + return Theme( + data: _theme, + child: Builder( + builder: (context) => widget.builder(context, _theme, colors), + ), + ); + } + + return const SizedBox(); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('colors', colors)); + properties.add(DiagnosticsProperty('zetaTheme', zetaTheme)); + } +} diff --git a/lib/zeta.dart b/lib/zeta.dart deleted file mode 100644 index 41beb933..00000000 --- a/lib/zeta.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -import '../zeta_flutter.dart'; - -/// Wrapper widget for apps using zeta. -/// -/// Please place this widget at the top level of the application. -/// Typically this would be wrapping a [MaterialApp], [WidgetsApp] or [CupertinoApp]. -/// -/// Without this widget in the tree, theming and colors may not work. -class Zeta extends StatefulWidget { - /// Base theme for the app. Zeta styles will be applied on top of this. - final ZetaThemeData? theme; - - /// Override for custom colors. - final ZetaColors colors; - - /// Builder for the app. - /// - /// Returns theme and colors. - final Widget Function(BuildContext, ThemeData, ZetaColors) builder; - - /// Constructor for [Zeta]. - const Zeta({required this.builder, this.theme, this.colors = const ZetaColors(), super.key}); - @override - State createState() => ZetaState(); -} - -/// State for [Zeta]. -class ZetaState extends State { - ZetaColors _colors = const ZetaColors(); - GlobalKey _key = GlobalKey(); - ThemeData _theme = ThemeData(); - bool _ready = false; - - /// Colors for app. - /// - /// Access using `ZetaColors.of(context)`. - ZetaColors get colors => _colors; - - /// Sets colors for app. - /// - /// Access using `ZetaColors.setColors(context, colors)`. - /// - /// When called, resets top-level key to force UI rebuild of app. - set colors(ZetaColors value) { - if (_colors != value) { - setState(() { - _colors = value; - _key = GlobalKey(); - _theme = ZetaTheme.builder(initialTheme: (widget.theme ?? const ZetaThemeData()).copyWith(zetaColors: value)); - }); - } - } - - @override - void initState() { - super.initState(); - - colors = widget.colors; - _theme = ZetaTheme.zetaLight(initialTheme: widget.theme); - _ready = true; - } - - @override - void didUpdateWidget(Zeta oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.colors != widget.colors) { - colors = widget.colors; - } - } - - @override - Widget build(BuildContext context) { - if (_ready) { - return Material( - child: DefaultTextStyle( - style: ZetaText.zetaBodyMedium.copyWith( - decoration: TextDecoration.none, - ), - child: Theme( - key: _key, - data: _theme, - child: Builder(builder: (context) => widget.builder(context, _theme, colors)), - ), - ), - ); - } - - return const SizedBox(); - } -} diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index 4c18476d..b657f5bc 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -1,11 +1,11 @@ /// Zebra Design System (Zeta) - Flutter Component Library library zeta_flutter; -export 'components/grid.dart'; -export 'components/spacing.dart'; -export 'components/text.dart'; -export 'theme/constants.dart'; -export 'theme/theme.dart'; -export 'tokens.dart'; -export 'utils/extensions.dart'; -export 'zeta.dart'; +export 'src/components/grid.dart'; +export 'src/components/spacing.dart'; +export 'src/components/text.dart'; +export 'src/theme/constants.dart'; +export 'src/theme/theme.dart'; +export 'src/tokens.dart'; +export 'src/utils/extensions.dart'; +export 'src/zeta.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 3d05ee2c..a903adee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,10 @@ name: zeta_flutter +version: 0.0.1+12 description: Zebra Design System (Zeta) - Flutter Component Library -version: 0.0.1+11 -repository: "https:/github.com/zebradevs/zeta-flutter" +homepage: https://github.com/zebradevs/zeta_flutter +repository: https://github.com/zebradevs/zeta_flutter +issue_tracker: https://github.com/zebradevs/zeta_flutter/issues +documentation: https://zeta-ds.web.app/flutter/dartdoc/index.html platforms: android: @@ -11,7 +14,6 @@ platforms: windows: linux: - environment: sdk: ">=3.0.1 <4.0.0" flutter: ">=3.7.0" @@ -28,10 +30,10 @@ flutter: fonts: - family: IBMPlexSans fonts: - - asset: lib/assets/fonts/IBMPlexSans-Light.otf + - asset: lib/src/assets/fonts/IBMPlexSans-Light.otf weight: 300 - - asset: lib/assets/fonts/IBMPlexSans-Regular.otf + - asset: lib/src/assets/fonts/IBMPlexSans-Regular.otf weight: 400 - - asset: lib/assets/fonts/IBMPlexSans-Medium.otf + - asset: lib/src/assets/fonts/IBMPlexSans-Medium.otf weight: 500 uses-material-design: true From 785b21cc454acff23a081cf43b5ed469db9ba756 Mon Sep 17 00:00:00 2001 From: Prashant Sawant <75819349+ps9310@users.noreply.github.com> Date: Thu, 9 Nov 2023 08:38:12 -0500 Subject: [PATCH 2/6] System theme support (#1) * chore: Tidy, reorganise and prepare repo docs: update changelog and documentation feat(type): Add xSmall and conform to latest figma designs. * "Refactored the ZetaColors class for better customization The ZetaColors class was heavily refactored for better customization of variables such as brightness, contrast, color swatches, and additional color attributes. Several fields were made final for the overall class safety. This change improves color control on different themes for the application." * "Updated the ZetaColor and Theme setup to use InheritedWidget The ZetaDefaults class was updated to Zeta inheriting from InheritedWidget. This change allows easy access to the Zeta theme settings (contrast, theme mode, theme data, color set) from anywhere in the widget tree. The ZetaAppBuilder function was updated to take in ThemeData and ThemeMode. The ZetaProvider was added to provide Zeta theming and contrast data down the widget tree. The code for the color and typography examples was adjusted to use the new Zeta context extension, instead of using Theme.of(context) to get colorScheme. This change was crucial to simplify the process of adapting the application visuals to different themes." * "Improve theme management functionality in Zeta Removed code concerning getting a color's RGB hex code from 'color_extensions.dart', as it was seldom used. Updated 'zeta_flutter.dart' to unhide ZetaColorGetters. Adaptations in 'zeta.dart' included switching mediaBrightness to _mediaBrightness for internal use and adding methods for accurate determination of color set and brightness settings based on the theme mode. Also, ZetaProvider was updated for 'system' theme mode support. example/lib/main.dart and example/lib/widgets.dart were updated to support these changes, including UI updates for seamless theme switching." * "Add theme update function and extend ZetaColorGetters Implemented a method in 'zeta.dart' to support updating the current theme data dynamically. Extended 'color_scheme.dart' by introducing _ZetaColorProperties and updating ZetaColorGetters. These changes increase flexibility for theme management and provide a structured and accessible way to get Zeta colors through the theme context." * Remove theme_extensions.dart and move contents to colors.dart Theme extensions were deleted and its contents were moved to colors.dart to consolidate all color-related codes in one file for easier navigation and editing. Additional enhancements include optimizing color assignments and making ZetaColors immutable for more robust color management. * Refactor code for color theme and add theme switcher Refactored codebase to improve the color theme handling: relocated theme related methods to colors.dart from theme_extensions.dart for consolidated color theme data. Optimized color assignments by leveraging the 'copyWith' method, allowing more efficient color management. Introduced the immutability of ZetaColors to enhance robustness. Bumped version in pubspec.yaml to 0.0.1+13 due to these changes. Renamed theme.dart to theme_data.dart for more semantic file naming. Added 'identifier' to the ZetaThemeData for easier theme identification. The visible application change is an added ThemeSwitcher in the example app, offering a UI to switch between different predefined themes. * Add ZetaThemeService and theme switcher in example app Implemented ZetaThemeService as an abstract class, providing structure for loading and saving themes within the app. Removed an obsolete comment within the contrast.dart and made necessary imports in zeta.dart. Asynchronous theme loading is added during app startup and saving is done upon theme updates. Also, for user-interaction, an exclusive ThemeSwitcher widget is added in the example app allowing users to select between available themes. This improves user experience, and optimizes theme handling and application performance. * Refactor color swatch generation to utilize zeta Refactored color swatch generation in color_example.dart to use Zeta instead of directly using the Theme. Now the brightness for ZetaColorSwatch is being pulled from zeta object rather than theme. This ensures consistency across different parts of the application where Zeta is used. Also changed theme.colorScheme.surface to colors.surfacePrimary for better readability, and alignment with use of zeta object. * Add icon colors to color scheme Extended the color scheme in colors.dart to include default, subtle, disabled, and inverse icon colors. These were added to ensure consistent icon colors across the application and support dark mode by allowing inverted color swatches. * Refactor theme switch settings and add new features Renamed 'theme_switch.dart' to 'theme_color_switch.dart' and added two new files 'theme_contrast_switch.dart' and 'theme_mode_switch.dart' in order to separate the theme settings logically into distinct features - Theme Color Switch, Theme Contrast Switch and Theme Mode Switch respectively. Also, the theme application feature has been refactored within 'widgets.dart' to use the newly created theme features instead of the old theme switch. This enhances modularity and the user's ability to switch theme settings easily. * Update method naming for consistent architecture in text.dart Changed the method name 'withColor' to 'themeWithColor' in text.dart for consistency with other part of the architecture and for better readability. This change supports the shift towards a consistently designed application architecture and helps developers easily decipher the role of the method in the code. * Update color scheme mapping and library version in colors.dart and pubspec.lock Refined the color mapping in ZetaColorScheme in colors.dart by replacing effectiveSurfaceTertiary with textDefault, enhancing the clarity of backdrop's color role. Concurrently, version of multiple dependencies in pubspec.lock are updated to benefit from recent fixes and improvements in those libraries. * Change `Color` to `ZetaColorSwatch` in theme files Adjusted the class references in colors.dart from `Color` to `ZetaColorSwatch` to provide a more consistent color swatch across the app. The swatch allows for more flexibility in using color variations. Adjustments were also made in color_scheme.dart and color_swatch.dart to include better explanatory messages and use standard dart documentation format. Changes in custom_docs/components/Color/flutter.md were made to align with these updates. * Enhance contrast and color handling in theme files Removed 'flutter.md' as it is no longer required due to improvements made in contrast and color handling. For better accessibility support, 'contrast.dart' was refactored for better contrast handling and 'color_extensions.dart' now includes a mechanism to generate color swatch based on contrast ratio. Also, 'zeta.dart' was updated to adapt to the system's brightness providing better user experience. Overall, these adjustments aim to enhance accessibility and user experience, apart from simplifying the codebase. * Add LICENSE-3RD-PARTY for third-party libraries Introduced license details for third-party libraries used in the project. MIT license applies to 'tinycolor' and SIL Open Font License applies to 'IBMPlexSans'. This ensures proper acknowledgement and licensure compliance for used third-party resources. --------- Co-authored-by: Luke Walton --- LICENSE-3RD-PARTY | 9 + README.md | 77 +- custom_docs/components/Color/flutter.md | 8 +- example/android/build.gradle | 2 +- example/ios/Podfile.lock | 2 +- example/lib/home.dart | 46 +- example/lib/main.dart | 21 +- example/lib/pages/color_example.dart | 239 +-- example/lib/pages/grid_example.dart | 20 +- example/lib/pages/spacing_example.dart | 15 +- example/lib/pages/theme_color_switch.dart | 69 + example/lib/pages/theme_constrast_switch.dart | 56 + example/lib/pages/theme_mode_switch.dart | 60 + example/lib/pages/typography_example.dart | 29 +- example/lib/widgets.dart | 38 +- example/pubspec.lock | 18 +- example/test/color_test.dart | 10 +- example/test/test_components.dart | 13 +- example/test/typography_test.dart | 4 +- .../components/color_widgetbook.dart | 33 +- .../components/typography_widgetbook.dart | 7 +- example/widgetbook/widgetbook.dart | 2 +- lib/src/components/text.dart | 36 +- lib/src/theme/breakpoints.dart | 45 +- lib/src/theme/color_extensions.dart | 305 ++++ lib/src/theme/color_scheme.dart | 170 +++ lib/src/theme/color_swatch.dart | 197 +++ lib/src/theme/colors.dart | 1292 ++++++++++------- lib/src/theme/colors_base.dart | 280 ++-- lib/src/theme/constants.dart | 20 + lib/src/theme/contrast.dart | 149 ++ lib/src/theme/theme.dart | 128 -- lib/src/theme/theme_data.dart | 97 ++ lib/src/theme/theme_service.dart | 25 + lib/src/tokens.dart | 3 - lib/src/zeta.dart | 381 ++++- lib/zeta_flutter.dart | 6 +- pubspec.yaml | 2 +- 38 files changed, 2743 insertions(+), 1171 deletions(-) create mode 100644 LICENSE-3RD-PARTY create mode 100644 example/lib/pages/theme_color_switch.dart create mode 100644 example/lib/pages/theme_constrast_switch.dart create mode 100644 example/lib/pages/theme_mode_switch.dart create mode 100644 lib/src/theme/color_extensions.dart create mode 100644 lib/src/theme/color_scheme.dart create mode 100644 lib/src/theme/color_swatch.dart create mode 100644 lib/src/theme/contrast.dart delete mode 100644 lib/src/theme/theme.dart create mode 100644 lib/src/theme/theme_data.dart create mode 100644 lib/src/theme/theme_service.dart diff --git a/LICENSE-3RD-PARTY b/LICENSE-3RD-PARTY new file mode 100644 index 00000000..004ba20a --- /dev/null +++ b/LICENSE-3RD-PARTY @@ -0,0 +1,9 @@ +----------------------------------------------------------------------------- + MIT + applies to: + - tinycolor + + SIL Open Font License, Version 1.1 + applies to: + - IBMPlexSans +----------------------------------------------------------------------------- \ No newline at end of file diff --git a/README.md b/README.md index b2b42122..eb8c75bd 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,87 @@ Zeta is the new, formal, standardized Zebra Design System based off the successes of ZDS (Zebra Design System). -Note: This package is in pre-release, and so many aspects are incomplete. +> 🚧 **Note**: This package is in pre-release, and so many aspects are incomplete. -# Usage +## Installation -[Install zeta_flutter](https://pub.dev/packages/zeta_flutter/install) +To install `zeta_flutter`, follow the instructions [here](https://pub.dev/packages/zeta_flutter/install). -Zeta extends the use of Flutter's built in theming tools, and so to work correctly your app needs to be wrapped with the zeta theme as such: +## Usage +Zeta offers flexibility in theming through its `ZetaProvider` widget. Here's a breakdown of its features: + +### Setting the Initial Theme Mode + +Zeta allows you to specify an initial theme mode for your app, which can be one of the following: + +- `ThemeMode.system`: Adheres to the system's theme. +- `ThemeMode.light`: Uses the light theme mode. +- `ThemeMode.dark`: Uses the dark theme mode. + +By default, the theme mode is set to `ThemeMode.system`. + +```dart +initialThemeMode: ThemeMode.light +``` + +### Providing Initial Theme Data + +You can provide the initial theme data for the app which contains all the theming information. If you don't specify one, it will default to a basic instance of `ZetaThemeData`. + +```dart +initialThemeData: ZetaThemeData() +``` + +### Setting the Initial Contrast + +Zeta also lets you define the initial contrast setting for your app. By default, it's set to `ZetaContrast.aa`. + +```dart +initialContrast: ZetaContrast.aa +``` + +### Building Your App with Zeta Theming + +The `builder` function is used to construct the widget tree with the provided theming information. This function is expected to receive a `BuildContext`, `ZetaThemeData`, and `ThemeMode` as arguments, and it should return a `Widget`. + +```dart +builder: (context, themeData, themeMode) { + // Your app's widget tree here +} +``` + +### Constructing the ZetaProvider + +To tie everything together, use the `ZetaProvider` constructor. The `builder` argument is mandatory, while the others are optional but allow you to set initial values: ```dart @override Widget build(BuildContext context) { - return Zeta( - builder: (context, theme, colors) { - return MaterialApp.router(theme: theme, routerConfig: router); + return ZetaProvider( + builder: (context, themeData, themeMode) { + final dark = themeData.colorsDark.toScheme(); + final light = themeData.colorsLight.toScheme(); + return MaterialApp.router( + routerConfig: router, + themeMode: themeMode, + theme: ThemeData( + fontFamily: themeData.fontFamily, + scaffoldBackgroundColor: light.background, + colorScheme: light, + ), + darkTheme: ThemeData( + fontFamily: themeData.fontFamily, + scaffoldBackgroundColor: dark.background, + colorScheme: dark, + ), + ); }, ); } ``` -This returns the Zeta theme and colors, which will be used across the app. Custom `ThemeData` and `ZetaColor` objects can be passed in to apply custom themes and colors. - +With these configurations, Zeta makes it easy to achieve consistent theming throughout your Flutter application. ## Viewing the components To view examples of all the components in the library, you can run the example app in this repo or go to [Zeta](https://zeta-ds.web.app/) diff --git a/custom_docs/components/Color/flutter.md b/custom_docs/components/Color/flutter.md index 86eec97e..b183f74b 100644 --- a/custom_docs/components/Color/flutter.md +++ b/custom_docs/components/Color/flutter.md @@ -21,13 +21,13 @@ class MyApp extends StatelessWidget{ /// Build colors object with custom colors. final ZetaColors colors = ZetaColors( - /// Add custom colors here. + /// Add custom colors here. ); /// Wrap whole app with [Zeta] to provide theming. return Zeta( - colors: colors, - builder: (BuildContext context, ThemeData theme, ZetaColors colors) => ZetaColorExample(), + colors: colors, + builder: (BuildContext context, ThemeData theme, ZetaColors colors) => ZetaColorExample(), ); } } @@ -42,7 +42,7 @@ class ZetaColorExample extends StatelessWidget{ final ZetaColors colors = ZetaColors.of(context); return Container( - color: colors.red, + color: colors.red, ); } } diff --git a/example/android/build.gradle b/example/android/build.gradle index 58a8c74b..713d7f6e 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index d403d6cb..f5c285f1 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -20,4 +20,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 -COCOAPODS: 1.12.1 +COCOAPODS: 1.13.0 diff --git a/example/lib/home.dart b/example/lib/home.dart index b3728c64..ad1381d3 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -4,20 +4,21 @@ import 'package:zeta_example/pages/color_example.dart'; import 'package:zeta_example/pages/grid_example.dart'; import 'package:zeta_example/pages/spacing_example.dart'; import 'package:zeta_example/pages/typography_example.dart'; +import 'package:zeta_example/widgets.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; class Component { final String name; - final Widget page; + final WidgetBuilder pageBuilder; final List children; - Component(this.name, this.page, [this.children = const []]); + Component(this.name, this.pageBuilder, [this.children = const []]); } final List components = [ - Component(GridExample.name, const GridExample()), - Component(SpacingExample.name, const SpacingExample()), - Component(TypographyExample.name, const TypographyExample()), - Component(ColorExample.name, const ColorExample()), + Component(GridExample.name, (context) => const GridExample()), + Component(SpacingExample.name, (context) => const SpacingExample()), + Component(TypographyExample.name, (context) => const TypographyExample()), + Component(ColorExample.name, (context) => const ColorExample()), ]; class Home extends StatefulWidget { @@ -36,8 +37,8 @@ final GoRouter router = GoRouter( ...components.map( (e) => GoRoute( path: e.name, - builder: (_, __) => e.page, - routes: e.children.map((f) => GoRoute(path: f.name, builder: (_, __) => f.page)).toList(), + builder: (_, __) => e.pageBuilder.call(_), + routes: e.children.map((f) => GoRoute(path: f.name, builder: (_, __) => f.pageBuilder(_))).toList(), ), ) ], @@ -49,24 +50,17 @@ class _HomeState extends State { @override Widget build(BuildContext context) { final items = components..sort((a, b) => a.name.compareTo(b.name)); - - final colors = ZetaColors.of(context); - return Scaffold( - appBar: AppBar( - title: const Text('Zeta'), - backgroundColor: colors.primary, - ), - body: ColoredBox( - color: colors.background, - child: ListView.builder( - itemCount: items.length, - itemBuilder: (context, index) { - return ListTile( - title: ZetaText(items[index].name), - onTap: () => context.go('/${items[index].name}'), - ); - }, - ), + return ExampleScaffold( + name: 'Zeta', + child: ListView.builder( + padding: EdgeInsets.all(Dimensions.s), + itemCount: items.length, + itemBuilder: (context, index) { + return ListTile( + title: ZetaText(items[index].name), + onTap: () => context.go('/${items[index].name}'), + ); + }, ), ); } diff --git a/example/lib/main.dart b/example/lib/main.dart index b742e1db..19c69098 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -9,9 +9,24 @@ class ZetaExample extends StatelessWidget { @override Widget build(BuildContext context) { - return Zeta( - builder: (context, theme, colors) { - return MaterialApp.router(theme: theme, routerConfig: router); + return ZetaProvider( + builder: (context, themeData, themeMode) { + final dark = themeData.colorsDark.toScheme(); + final light = themeData.colorsLight.toScheme(); + return MaterialApp.router( + routerConfig: router, + themeMode: themeMode, + theme: ThemeData( + fontFamily: themeData.fontFamily, + scaffoldBackgroundColor: light.background, + colorScheme: light, + ), + darkTheme: ThemeData( + fontFamily: themeData.fontFamily, + scaffoldBackgroundColor: dark.background, + colorScheme: dark, + ), + ); }, ); } diff --git a/example/lib/pages/color_example.dart b/example/lib/pages/color_example.dart index 633b2519..1093e8bf 100644 --- a/example/lib/pages/color_example.dart +++ b/example/lib/pages/color_example.dart @@ -5,6 +5,7 @@ import '../widgets.dart'; class ColorExample extends StatefulWidget { static const String name = 'Color'; + const ColorExample({super.key}); @override @@ -16,7 +17,8 @@ class _ColorExampleState extends State { @override Widget build(BuildContext context) { - final ZetaColors colors = ZetaColors.of(context); + final zeta = Zeta.of(context); + final colors = zeta.colors; return LayoutBuilder( builder: (context, constraints) { final Map swatches = { @@ -33,29 +35,68 @@ class _ColorExampleState extends State { }; final Map generatedSwatches = { - 'Gen-Blue': colors.blue.zetaColorSwatch, + 'Gen-Blue': ZetaColorSwatch.fromColor( + colors.blue, + brightness: zeta.brightness, + contrast: colors.contrast, + ), 'Blue': colors.blue, - 'Gen-Green': colors.green.zetaColorSwatch, + 'Gen-Green': ZetaColorSwatch.fromColor( + colors.green, + brightness: zeta.brightness, + contrast: colors.contrast, + ), 'Green': colors.green, - 'Gen-Red': colors.red.zetaColorSwatch, + 'Gen-Red': ZetaColorSwatch.fromColor( + colors.red, + brightness: zeta.brightness, + contrast: colors.contrast, + ), 'Red': colors.red, - 'Gen-Orange': colors.orange.zetaColorSwatch, + 'Gen-Orange': ZetaColorSwatch.fromColor( + colors.orange, + brightness: zeta.brightness, + contrast: colors.contrast, + ), 'Orange': colors.orange, - 'Gen-Purple': colors.purple.zetaColorSwatch, + 'Gen-Purple': ZetaColorSwatch.fromColor( + colors.purple, + brightness: zeta.brightness, + contrast: colors.contrast, + ), 'Purple': colors.purple, - 'Gen-Yellow': colors.yellow.zetaColorSwatch, + 'Gen-Yellow': ZetaColorSwatch.fromColor( + colors.yellow, + brightness: zeta.brightness, + contrast: colors.contrast, + ), 'Yellow': colors.yellow, - 'Gen-Teal': colors.teal.zetaColorSwatch, + 'Gen-Teal': ZetaColorSwatch.fromColor( + colors.teal, + brightness: zeta.brightness, + contrast: colors.contrast, + ), 'Teal': colors.teal, - 'Gen-Pink': colors.pink.zetaColorSwatch, + 'Gen-Pink': ZetaColorSwatch.fromColor( + colors.pink, + brightness: zeta.brightness, + contrast: colors.contrast, + ), 'Pink': colors.pink, - 'Gen-Grey Warm': colors.warm.zetaColorSwatch, + 'Gen-Grey Warm': ZetaColorSwatch.fromColor( + colors.warm, + brightness: zeta.brightness, + contrast: colors.contrast, + ), 'Grey Warm': colors.warm, - 'Gen-Grey Cool': colors.cool.zetaColorSwatch, + 'Gen-Grey Cool': ZetaColorSwatch.fromColor( + colors.cool, + brightness: zeta.brightness, + contrast: colors.contrast, + ), 'Grey Cool': colors.cool, }; - final Map bw = {'white': colors.white, 'black': colors.black}; final Map textIcon = { 'textDefault': colors.textDefault, 'textSubtle': colors.textSubtle, @@ -84,12 +125,7 @@ class _ColorExampleState extends State { final Map primaries = { 'primaryColor': colors.primary, - 'primarySelected': colors.primary.selected, - 'primaryHover': colors.primary.hover, - 'primaryText': colors.primary.text, - 'primaryBorder': colors.primary.border, - 'primarySubtle': colors.primary.subtle, - 'primarySurface': colors.primary.surface, + 'secondaryColor': colors.secondary, }; final Map alerts = { @@ -101,108 +137,97 @@ class _ColorExampleState extends State { return ExampleScaffold( name: ColorExample.name, - actions: [ - Center(child: ZetaText('DarkMode', textColor: Colors.white)), - Switch.adaptive( - value: colors.isDarkMode, - onChanged: (isDarkMode) => ZetaColors.setColors(context, colors.copyWith(isDarkMode: isDarkMode)), - ), - const SizedBox(width: Dimensions.l), - const Center(child: ZetaText('AAA ', textColor: Colors.white)), - Switch.adaptive( - value: colors.isAAA, - onChanged: (isAAA) => ZetaColors.setColors(context, colors.copyWith(isAAA: isAAA)), - ), - ], - child: Column( - children: [ - MyRow(children: bw, title: 'Black and white'), - MyRow(children: textIcon, title: 'Text and icon styles'), - MyRow(children: border, title: 'Border styles'), - MyRow(children: links, title: 'Links'), - MyRow(children: backdrop, title: 'Backdrop colors'), - MyRow(children: primaries, title: 'Primary colors'), - MyRow(children: alerts, title: 'Alert colors'), - Row(children: [ZetaText.displayMedium('Full color swatches')]).squish(Dimensions.x8), - ...swatches.entries.map( - (value) => Row( - children: List.generate(10, (index) => 100 - (10 * index)) - .map( - (e) => Expanded( - child: Container( - height: constraints.maxWidth / 10, - color: value.value[e], - child: FittedBox( - fit: BoxFit.scaleDown, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - DefaultTextStyle( - style: ZetaText.zetaBodyMedium - .copyWith(color: calculateTextColor(value.value[e] ?? Colors.white)), - child: Column( - children: [ - Text('${value.key.toLowerCase().replaceAll(' ', '')}-$e'), - Text(value.value[e].toString().replaceAll('Color(0xff', '#').substring(0, 7)), - ], - ), - ), - ], - ), - ), - ), - ), - ) - .toList(), - ), - ), - ElevatedButton( - onPressed: () => setState(() => showGeneratedColors = !showGeneratedColors), - child: const Text('Toggle generated colors').square(Dimensions.s), - ).square(Dimensions.s), - if (showGeneratedColors) - Row(children: [ZetaText.displayMedium('Generated color swatches')]).squish(Dimensions.x8), - if (showGeneratedColors) - ...generatedSwatches.entries.map( + child: SingleChildScrollView( + padding: EdgeInsets.all(Dimensions.s), + child: Column( + children: [ + MyRow(children: textIcon, title: 'Text and icon styles'), + MyRow(children: border, title: 'Border styles'), + MyRow(children: links, title: 'Links'), + MyRow(children: backdrop, title: 'Backdrop colors'), + MyRow(children: primaries, title: 'Primary colors'), + MyRow(children: alerts, title: 'Alert colors'), + Row(children: [ZetaText.displayMedium('Full color swatches')]).squish(Dimensions.x8), + ...swatches.entries.map( (value) => Row( - children: List.generate(11, (index) => 110 - (10 * index)) + children: List.generate(10, (index) => 100 - (10 * index)) .map( (e) => Expanded( child: Container( height: constraints.maxWidth / 10, - color: e == 110 ? colors.surface : value.value[e], - child: e == 110 - ? SizedBox() - : FittedBox( - fit: BoxFit.scaleDown, + color: value.value[e], + child: FittedBox( + fit: BoxFit.scaleDown, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + DefaultTextStyle( + style: ZetaText.zetaBodyMedium + .copyWith(color: calculateTextColor(value.value[e] ?? Colors.white)), child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - DefaultTextStyle( - style: ZetaText.zetaBodyMedium - .copyWith(color: calculateTextColor(value.value[e] ?? Colors.white)), - child: Column( - children: [ - Text('${value.key.toLowerCase().replaceAll(' ', '')}-$e'), - Text( - value.value[e] - .toString() - .replaceAll('Color(0xff', '#') - .substring(0, 7), - ), - ], - ), - ), + Text('${value.key.toLowerCase().replaceAll(' ', '')}-$e'), + Text(value.value[e].toString().replaceAll('Color(0xff', '#').substring(0, 7)), ], ), ), + ], + ), + ), ), ), ) .toList(), ), ), - ], + ElevatedButton( + onPressed: () => setState(() => showGeneratedColors = !showGeneratedColors), + child: const Text('Toggle generated colors').square(Dimensions.s), + ).square(Dimensions.s), + if (showGeneratedColors) + Row(children: [ZetaText.displayMedium('Generated color swatches')]).squish(Dimensions.x8), + if (showGeneratedColors) + ...generatedSwatches.entries.map( + (value) => Row( + children: List.generate(11, (index) => 110 - (10 * index)) + .map( + (e) => Expanded( + child: Container( + height: constraints.maxWidth / 10, + color: e == 110 ? colors.surfacePrimary : value.value[e], + child: e == 110 + ? SizedBox() + : FittedBox( + fit: BoxFit.scaleDown, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + DefaultTextStyle( + style: ZetaText.zetaBodyMedium + .copyWith(color: calculateTextColor(value.value[e] ?? Colors.white)), + child: Column( + children: [ + Text('${value.key.toLowerCase().replaceAll(' ', '')}-$e'), + Text( + value.value[e] + .toString() + .replaceAll('Color(0xff', '#') + .substring(0, 7), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ) + .toList(), + ), + ), + ], + ), ), ); }, @@ -213,6 +238,7 @@ class _ColorExampleState extends State { class MyRow extends StatelessWidget { final Map children; final String title; + const MyRow({super.key, required this.children, required this.title}); @override @@ -225,16 +251,17 @@ class MyRow extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - ZetaText.labelLarge(title, textColor: ZetaColors.of(context).textDefault), + ZetaText.labelLarge(title, textColor: Zeta.of(context).colors.textDefault), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: children.entries .map( - (e) => Container( + (e) => AnimatedContainer( height: 160, width: 160, color: e.value, + duration: const Duration(milliseconds: 250), child: FittedBox( fit: BoxFit.scaleDown, child: Column( diff --git a/example/lib/pages/grid_example.dart b/example/lib/pages/grid_example.dart index d574f5af..fd10ed3d 100644 --- a/example/lib/pages/grid_example.dart +++ b/example/lib/pages/grid_example.dart @@ -11,6 +11,7 @@ class GridExample extends StatelessWidget { static const List asymmetrical = [11, 10, 9, 8, 7, 5, 4, 3, 2, 1]; const GridExample({super.key}); + @override Widget build(BuildContext context) { final List gridItems = List.generate(20, (index) => GridItem(label: (index + 1).toString())); @@ -78,9 +79,12 @@ class GridExample extends StatelessWidget { return ExampleScaffold( name: name, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [...examples.map(ExampleBuilder.new)], + child: SingleChildScrollView( + padding: EdgeInsets.all(Dimensions.s), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [...examples.map(ExampleBuilder.new)], + ), ), ); } @@ -89,16 +93,20 @@ class GridExample extends StatelessWidget { class GridItem extends StatelessWidget { final String label; final double? width; + const GridItem({this.label = '', this.width, super.key}); @override Widget build(BuildContext context) { - final ZetaColors colors = ZetaColors.of(context); + final colors = Zeta.of(context).colors; return Container( height: 80, width: width, - decoration: BoxDecoration(border: Border.all(color: colors.blue.border), color: colors.blue.shade20), - child: ZetaText(label), + decoration: BoxDecoration(border: Border.all(color: colors.blue.border), color: colors.blue.surface), + child: Padding( + padding: const EdgeInsets.only(left: 8), + child: ZetaText.bodyLarge(label, textColor: colors.blue.text), + ), ); } } diff --git a/example/lib/pages/spacing_example.dart b/example/lib/pages/spacing_example.dart index a8cfc223..ca7a82ce 100644 --- a/example/lib/pages/spacing_example.dart +++ b/example/lib/pages/spacing_example.dart @@ -14,11 +14,13 @@ class _TypeEx { final String name; final ZetaSpacingType? value; final String description; + const _TypeEx({required this.name, required this.value, required this.description}); } class SpacingExample extends StatelessWidget { static const String name = 'Spacing'; + const SpacingExample({super.key}); static const List<_SizeEx> _sizes = [ @@ -81,9 +83,13 @@ class SpacingExample extends StatelessWidget { Widget build(BuildContext context) { return ExampleScaffold( name: name, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [..._x.map((e) => e == null ? const Divider() : ExampleBuilder(e))]..removeLast(), + child: ListView.builder( + padding: EdgeInsets.all(Dimensions.s), + itemCount: _x.length, + itemBuilder: (context, index) { + final e = _x[index]; + return e == null ? const Divider() : ExampleBuilder(e); + }, ), ); } @@ -92,6 +98,7 @@ class SpacingExample extends StatelessWidget { class _SpaceExample extends StatelessWidget { final double size; final ZetaSpacingType type; + const _SpaceExample({required this.size, required this.type}); @override @@ -100,7 +107,7 @@ class _SpaceExample extends StatelessWidget { children: [ Expanded( child: ColoredBox( - color: ZetaColors.of(context).blue.shade20, + color: Zeta.of(context).colors.blue.shade20, child: ZetaSpacing(const SpacingItem(), size: size, type: type), ), ), diff --git a/example/lib/pages/theme_color_switch.dart b/example/lib/pages/theme_color_switch.dart new file mode 100644 index 00000000..a2ca12fc --- /dev/null +++ b/example/lib/pages/theme_color_switch.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class ZetaThemeColorSwitch extends StatelessWidget { + ZetaThemeColorSwitch({super.key}); + + late final _themes = { + "default": ZetaThemeData(), + "teal": ZetaThemeData( + identifier: 'teal', + primary: ZetaColorBase.teal, + ), + "yellow": ZetaThemeData( + identifier: 'yellow', + primary: ZetaColorBase.yellow, + ), + "red": ZetaThemeData( + identifier: 'red', + primary: ZetaColorBase.red, + ), + "purple": ZetaThemeData( + identifier: 'purple', + primary: ZetaColorBase.purple, + ), + }; + + @override + Widget build(BuildContext context) { + var zeta = Zeta.of(context); + + ZetaColors primary(ZetaThemeData data) { + if (zeta.brightness == Brightness.light) { + return data.colorsLight; + } else { + return data.colorsDark; + } + } + + return DropdownButtonHideUnderline( + child: DropdownButton( + value: zeta.themeData.identifier, + elevation: 0, + isDense: true, + alignment: Alignment.center, + icon: SizedBox(width: 8), + dropdownColor: zeta.colors.borderDisabled, + items: _themes.entries.map((e) { + var zetaColors = primary(_themes[e.key]!); + var color = zetaColors.primary; + return DropdownMenuItem( + value: e.value.identifier, + alignment: Alignment.center, + child: CircleAvatar( + backgroundColor: color.surface, + foregroundColor: color, + child: Icon(Icons.color_lens, color: color), + ), + ); + }).toList(), + onChanged: (value) { + final theme = _themes[value]; + if (theme != null) { + ZetaProvider.of(context).updateThemeData(theme); + } + }, + ), + ); + } +} diff --git a/example/lib/pages/theme_constrast_switch.dart b/example/lib/pages/theme_constrast_switch.dart new file mode 100644 index 00000000..9d36c28f --- /dev/null +++ b/example/lib/pages/theme_constrast_switch.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class ZetaThemeContrastSwitch extends StatelessWidget { + ZetaThemeContrastSwitch({super.key}); + + late final _themes = [ + ZetaContrast.aa, + ZetaContrast.aaa, + ]; + + @override + Widget build(BuildContext context) { + var zeta = Zeta.of(context); + + ZetaColors zetaColors(ZetaContrast contrast) { + if (zeta.brightness == Brightness.light) { + return zeta.themeData.apply(contrast: contrast).colorsLight; + } else { + return zeta.themeData.apply(contrast: contrast).colorsDark; + } + } + + return DropdownButtonHideUnderline( + child: DropdownButton( + value: zeta.contrast, + elevation: 0, + isDense: true, + alignment: Alignment.center, + icon: SizedBox(width: 8), + dropdownColor: zeta.colors.borderDisabled, + items: _themes.map((e) { + final colors = zetaColors(e); + return DropdownMenuItem( + value: e, + alignment: Alignment.center, + child: CircleAvatar( + backgroundColor: colors.primary.surface, + foregroundColor: colors.primary, + child: ZetaText.bodyMedium( + e == ZetaContrast.aa ? 'AA' : 'AAA', + textColor: colors.primary, + fontWeight: FontWeight.w700, + ), + ), + ); + }).toList(), + onChanged: (value) { + if (value != null) { + ZetaProvider.of(context).updateContrast(value); + } + }, + ), + ); + } +} diff --git a/example/lib/pages/theme_mode_switch.dart b/example/lib/pages/theme_mode_switch.dart new file mode 100644 index 00000000..392d7e12 --- /dev/null +++ b/example/lib/pages/theme_mode_switch.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class ZetaThemeModeSwitch extends StatelessWidget { + ZetaThemeModeSwitch({super.key}); + + late final _themes = [ + ThemeMode.system, + ThemeMode.light, + ThemeMode.dark, + ]; + + @override + Widget build(BuildContext context) { + var zeta = Zeta.of(context); + + ZetaColors zetaColors(ThemeMode mode) { + if ((mode == ThemeMode.system && MediaQuery.of(context).platformBrightness == Brightness.light) || + mode == ThemeMode.light) { + return zeta.themeData.colorsLight; + } else { + return zeta.themeData.colorsDark; + } + } + + return DropdownButtonHideUnderline( + child: DropdownButton( + value: zeta.themeMode, + elevation: 0, + isDense: true, + alignment: Alignment.center, + icon: SizedBox(width: 8), + dropdownColor: zeta.colors.borderDisabled, + items: _themes.map((e) { + final colors = zetaColors(e); + return DropdownMenuItem( + value: e, + alignment: Alignment.center, + child: CircleAvatar( + backgroundColor: colors.primary.surface, + foregroundColor: colors.primary, + child: Icon( + e == ThemeMode.system + ? Icons.system_security_update_good + : e == ThemeMode.light + ? Icons.light_mode + : Icons.dark_mode, + color: colors.primary), + ), + ); + }).toList(), + onChanged: (value) { + if (value != null) { + ZetaProvider.of(context).updateThemeMode(value); + } + }, + ), + ); + } +} diff --git a/example/lib/pages/typography_example.dart b/example/lib/pages/typography_example.dart index 8be0e9b0..0ff46d3b 100644 --- a/example/lib/pages/typography_example.dart +++ b/example/lib/pages/typography_example.dart @@ -105,7 +105,7 @@ class TypographyExample extends StatelessWidget { @override Widget build(BuildContext context) { - final ZetaColors colors = ZetaColors.of(context); + final colors = Zeta.of(context).colors; final tokens = [ const ExampleModel( @@ -167,18 +167,21 @@ class TypographyExample extends StatelessWidget { return ExampleScaffold( name: name, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ZetaText.headingLarge('Text').inline(Dimensions.x10), - ...tokens.map(ExampleBuilder.new), - const Divider().squish(Dimensions.x4), - ZetaText.headingLarge('Universal sizes').inline(Dimensions.x10), - ...universalSizes.map(ExampleBuilder.new), - const Divider().squish(Dimensions.x4), - ZetaText.headingLarge('Dedicated sizes').inline(Dimensions.x10), - ...dedicatedSizes.map(ExampleBuilder.new), - ], + child: SingleChildScrollView( + padding: EdgeInsets.all(Dimensions.s), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ZetaText.headingLarge('Text').inline(Dimensions.x10), + ...tokens.map(ExampleBuilder.new), + const Divider().squish(Dimensions.x4), + ZetaText.headingLarge('Universal sizes').inline(Dimensions.x10), + ...universalSizes.map(ExampleBuilder.new), + const Divider().squish(Dimensions.x4), + ZetaText.headingLarge('Dedicated sizes').inline(Dimensions.x10), + ...dedicatedSizes.map(ExampleBuilder.new), + ], + ), ), ); } diff --git a/example/lib/widgets.dart b/example/lib/widgets.dart index ad7b375f..99c8da5d 100644 --- a/example/lib/widgets.dart +++ b/example/lib/widgets.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:zeta_example/pages/theme_color_switch.dart'; +import 'package:zeta_example/pages/theme_constrast_switch.dart'; +import 'package:zeta_example/pages/theme_mode_switch.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; class ExampleModel { @@ -21,6 +24,7 @@ class ExampleModel { class ExampleBuilder extends StatelessWidget { final ExampleModel model; + const ExampleBuilder(this.model, {super.key}); @override @@ -31,7 +35,7 @@ class ExampleBuilder extends StatelessWidget { Container( height: 7, width: 7, - decoration: const BoxDecoration(color: Colors.black, shape: BoxShape.circle), + decoration: BoxDecoration(color: Theme.of(context).colorScheme.onSurface, shape: BoxShape.circle), ).squish(Dimensions.x9).inline(Dimensions.x4), Expanded( child: Column( @@ -66,20 +70,24 @@ class ExampleScaffold extends StatelessWidget { @override Widget build(BuildContext context) { - final ZetaColors colors = ZetaColors.of(context); + var theme = Theme.of(context); + final colors = theme.colorScheme; return Scaffold( - appBar: AppBar(title: Text(name), actions: actions, backgroundColor: colors.primary), + appBar: AppBar( + centerTitle: false, + title: Text(name), + backgroundColor: colors.primary, + foregroundColor: colors.onPrimary, + actions: [ + ...actions, + ZetaThemeModeSwitch(), + ZetaThemeContrastSwitch(), + ZetaThemeColorSwitch(), + ], + ), body: SelectionArea( - child: ColoredBox( - color: colors.background, - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: Dimensions.x1, vertical: Dimensions.x6), - child: child, - ), - ), - ), + child: child, ), ); } @@ -88,11 +96,12 @@ class ExampleScaffold extends StatelessWidget { class CodeExample extends StatelessWidget { final String code; final bool fill; + const CodeExample({required this.code, this.fill = false, super.key}); @override Widget build(BuildContext context) { - final ZetaColors colors = ZetaColors.of(context); + var colors = Zeta.of(context).colors; final widget = Container( color: colors.surfaceDisabled, padding: Dimensions.x4.square, @@ -121,6 +130,7 @@ class CodeExample extends StatelessWidget { class FlutterWordMark extends StatelessWidget { final String text; final EdgeInsets padding; + const FlutterWordMark({ this.text = 'Flutter', this.padding = const EdgeInsets.symmetric(horizontal: Dimensions.x5, vertical: Dimensions.x2), @@ -130,7 +140,7 @@ class FlutterWordMark extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - color: ZetaColors.of(context).borderSubtle, + color: Zeta.of(context).colors.borderSubtle, padding: padding, child: ZetaText(text), ); diff --git a/example/pubspec.lock b/example/pubspec.lock index 6d0de80d..21e83efb 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -196,10 +196,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" path_provider_foundation: dependency: transitive description: @@ -236,10 +236,10 @@ packages: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" plugin_platform_interface: dependency: transitive description: @@ -337,18 +337,18 @@ packages: dependency: "direct dev" description: name: widgetbook - sha256: e2b6bbec0b7805037cb58937200461d0c19fcfdd50dc21fc3ceb558f2489a710 + sha256: dce7255b085d84ff1c5a941ce581d3bf24edd742902c22b4405279e5c3e22501 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.3.0" win32: dependency: transitive description: name: win32 - sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa" + sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" url: "https://pub.dev" source: hosted - version: "5.0.7" + version: "5.0.9" xdg_directories: dependency: transitive description: @@ -363,7 +363,7 @@ packages: path: ".." relative: true source: path - version: "0.0.1+12" + version: "0.0.1+13" sdks: dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.7.0" diff --git a/example/test/color_test.dart b/example/test/color_test.dart index fb5009a5..c6788f9f 100644 --- a/example/test/color_test.dart +++ b/example/test/color_test.dart @@ -1,10 +1,12 @@ +import 'dart:ui'; + import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; void main() { testWidgets('Dark mode value', (tester) async { final ZetaColors light = ZetaColors(); - final ZetaColors dark = ZetaColors(isDarkMode: true); + final ZetaColors dark = ZetaColors(brightness: Brightness.dark); expect(light.primary.shade10, dark.primary.shade100); expect(light.primary.shade20, dark.primary.shade90); @@ -15,10 +17,10 @@ void main() { testWidgets('AAA mode value', (tester) async { final ZetaColors aa = ZetaColors(); - final ZetaColors aaa = ZetaColors(isAAA: true); + final ZetaColors aaa = ZetaColors(brightness: Brightness.dark, contrast: ZetaContrast.aaa); - expect(aa.primary.primary, aa.primary.shade60); - expect(aaa.primary.primary, aaa.primary.shade80); + expect(aa.primary.value, aa.primary.shade60.value); + expect(aaa.primary.value, aaa.primary.shade80.value); }); testWidgets('Scheme generator', (tester) async { diff --git a/example/test/test_components.dart b/example/test/test_components.dart index 8e36ac93..117e5717 100644 --- a/example/test/test_components.dart +++ b/example/test/test_components.dart @@ -11,12 +11,21 @@ class TestWidget extends StatelessWidget { Widget build(BuildContext context) { final size = screenSize ?? const Size(1280, 720); - return Zeta( + return ZetaProvider( builder: (context, theme, __) { return Builder( builder: (context) { return MaterialApp( - theme: theme, + theme: ThemeData( + fontFamily: theme.fontFamily, + colorScheme: theme.colorsLight.toScheme(), + textTheme: ZetaText.textTheme, + ), + darkTheme: ThemeData( + fontFamily: theme.fontFamily, + colorScheme: theme.colorsDark.toScheme(), + textTheme: ZetaText.textTheme, + ), home: Scaffold( body: SizedBox( width: size.width, diff --git a/example/test/typography_test.dart b/example/test/typography_test.dart index c33abdf0..b20074f0 100644 --- a/example/test/typography_test.dart +++ b/example/test/typography_test.dart @@ -1,4 +1,4 @@ -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_example/pages/typography_example.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; @@ -28,7 +28,7 @@ void main() { fontSize: 100, fontStyle: FontStyle.italic, fontWeight: FontWeight.w500, - textColor: ZetaColors.of(context).textSubtle, + textColor: Zeta.of(context).colors.textSubtle, textDirection: TextDirection.rtl, first: true, last: true, diff --git a/example/widgetbook/components/color_widgetbook.dart b/example/widgetbook/components/color_widgetbook.dart index 1c70872a..3c4ef757 100644 --- a/example/widgetbook/components/color_widgetbook.dart +++ b/example/widgetbook/components/color_widgetbook.dart @@ -10,25 +10,11 @@ WidgetbookComponent colorWidgetBook() { WidgetbookUseCase( name: 'Colors', builder: (BuildContext context) { - final primary = context.knobs.color(label: 'Primary color', initialValue: ZetaColorBase.blue); - final ZetaColors colors; - if (primary != ZetaColorBase.blue) { - colors = ZetaColors( - isDarkMode: context.knobs.boolean(label: 'Dark mode'), - isAAA: context.knobs.boolean(label: 'AAA compliance'), - primary: ZetaColorSwatch.fromColor(primary), - ); - } else { - colors = ZetaColors( - isDarkMode: context.knobs.boolean(label: 'Dark mode'), - isAAA: context.knobs.boolean(label: 'AAA compliance'), - ); - } + final colors = Zeta.of(context).colors; return LayoutBuilder( builder: (context, constraints) { final Map swatches = { - 'Primary': colors.primary, 'Blue': colors.blue, 'Green': colors.green, 'Red': colors.red, @@ -40,7 +26,6 @@ WidgetbookComponent colorWidgetBook() { 'Grey Warm': colors.warm, 'Grey Cool': colors.cool, }; - final Map bw = {'white': colors.white, 'black': colors.black}; final Map textIcon = { 'textDefault': colors.textDefault, 'textSubtle': colors.textSubtle, @@ -66,15 +51,7 @@ WidgetbookComponent colorWidgetBook() { 'surfaceSelectedHovered': colors.surfaceSelectedHovered, 'surfaceSelected': colors.surfaceSelected, }; - final Map primaries = { - 'primaryColor': colors.primary.primary, - 'primarySelected': colors.primary.selected, - 'primaryHover': colors.primary.hover, - 'primaryText': colors.primary.text, - 'primaryBorder': colors.primary.border, - 'primarySubtle': colors.primary.subtle, - 'primarySurface': colors.primary.surface, - }; + final Map alerts = { 'positive': colors.positive, 'negative': colors.negative, @@ -82,22 +59,18 @@ WidgetbookComponent colorWidgetBook() { 'info': colors.info, }; - return Zeta( - colors: colors, + return ZetaProvider( builder: (context3, _, __) { return Container( padding: const EdgeInsets.symmetric(horizontal: Dimensions.l), - color: colors.background, child: SingleChildScrollView( child: Column( children: [ const SizedBox(height: Dimensions.l), - MyRow(children: bw, title: 'Black and white'), MyRow(children: textIcon, title: 'Text and icon styles'), MyRow(children: border, title: 'Border styles'), MyRow(children: links, title: 'Links'), MyRow(children: backdrop, title: 'Backdrop colors'), - MyRow(children: primaries, title: 'Primary colors'), MyRow(children: alerts, title: 'Alert colors'), Row(children: [ZetaText.displayMedium('Full color swatches')]).squish(Dimensions.x8), ...swatches.entries.map( diff --git a/example/widgetbook/components/typography_widgetbook.dart b/example/widgetbook/components/typography_widgetbook.dart index 0d638ff6..d7f5949b 100644 --- a/example/widgetbook/components/typography_widgetbook.dart +++ b/example/widgetbook/components/typography_widgetbook.dart @@ -25,7 +25,7 @@ WidgetbookComponent textWidgetBook() { WidgetbookUseCase( name: 'Universal sizes', builder: (context) => Container( - color: ZetaColors.of(context).background, + color: Theme.of(context).colorScheme.background, padding: const EdgeInsets.all(Dimensions.l), child: ZetaText( exampleText, @@ -53,7 +53,7 @@ WidgetbookComponent textWidgetBook() { WidgetbookUseCase( name: 'Dedicated sizes', builder: (context) => Container( - color: ZetaColors.of(context).background, + color: Theme.of(context).colorScheme.background, padding: const EdgeInsets.all(Dimensions.l), child: ZetaText( exampleText, @@ -74,9 +74,8 @@ class _TextWrapper extends StatelessWidget { @override Widget build(BuildContext context) { - final ZetaColors colors = ZetaColors.of(context); + final colors = Zeta.of(context).colors; return Container( - color: colors.background, padding: const EdgeInsets.all(Dimensions.l), child: ZetaText( context.knobs.string(label: 'Input text', initialValue: exampleText.split(',').first), diff --git a/example/widgetbook/widgetbook.dart b/example/widgetbook/widgetbook.dart index a41b230a..e1c128e3 100644 --- a/example/widgetbook/widgetbook.dart +++ b/example/widgetbook/widgetbook.dart @@ -13,7 +13,7 @@ class HotReload extends StatelessWidget { @override Widget build(BuildContext context) { - return Zeta( + return ZetaProvider( builder: (context, theme, colors) { return Widgetbook.material( directories: [ diff --git a/lib/src/components/text.dart b/lib/src/components/text.dart index 9b49f99f..ccf44fa8 100644 --- a/lib/src/components/text.dart +++ b/lib/src/components/text.dart @@ -37,23 +37,23 @@ class ZetaText extends StatelessWidget { ); /// Builds text theme for app based on an instance of [ZetaColors]. - static TextTheme textThemeBuilder(ZetaColors colors) { + static TextTheme themeWithColor(Color color) { return TextTheme( - displayLarge: zetaDisplayLarge.copyWith(color: colors.textDefault), - displayMedium: zetaDisplayMedium.copyWith(color: colors.textDefault), - displaySmall: zetaDisplaySmall.copyWith(color: colors.textDefault), - headlineLarge: zetaHeadingLarge.copyWith(color: colors.textDefault), - headlineMedium: zetaHeadingMedium.copyWith(color: colors.textDefault), - headlineSmall: zetaHeadingSmall.copyWith(color: colors.textDefault), - titleLarge: zetaTitleLarge.copyWith(color: colors.textDefault), - titleMedium: zetaTitleMedium.copyWith(color: colors.textDefault), - titleSmall: zetaTitleSmall.copyWith(color: colors.textDefault), - bodyLarge: zetaBodyLarge.copyWith(color: colors.textDefault), - bodyMedium: zetaBodyMedium.copyWith(color: colors.textDefault), - bodySmall: zetaBodySmall.copyWith(color: colors.textDefault), - labelLarge: zetaLabelLarge.copyWith(color: colors.textDefault), - labelMedium: zetaLabelMedium.copyWith(color: colors.textDefault), - labelSmall: zetaLabelSmall.copyWith(color: colors.textDefault), + displayLarge: zetaDisplayLarge.copyWith(color: color), + displayMedium: zetaDisplayMedium.copyWith(color: color), + displaySmall: zetaDisplaySmall.copyWith(color: color), + headlineLarge: zetaHeadingLarge.copyWith(color: color), + headlineMedium: zetaHeadingMedium.copyWith(color: color), + headlineSmall: zetaHeadingSmall.copyWith(color: color), + titleLarge: zetaTitleLarge.copyWith(color: color), + titleMedium: zetaTitleMedium.copyWith(color: color), + titleSmall: zetaTitleSmall.copyWith(color: color), + bodyLarge: zetaBodyLarge.copyWith(color: color), + bodyMedium: zetaBodyMedium.copyWith(color: color), + bodySmall: zetaBodySmall.copyWith(color: color), + labelLarge: zetaLabelLarge.copyWith(color: color), + labelMedium: zetaLabelMedium.copyWith(color: color), + labelSmall: zetaLabelSmall.copyWith(color: color), ); } @@ -411,7 +411,7 @@ class ZetaText extends StatelessWidget { ); String data = this.data ?? ''; - final Color color = textColor ?? ZetaColors.of(context).textDefault; + final Color color = textColor ?? Zeta.of(context).colors.textDefault; thisStyle = thisStyle.copyWith( fontSize: (fontSize ?? thisStyle.fontSize ?? tokens.Typography.defaultTextSize) * @@ -421,7 +421,6 @@ class ZetaText extends StatelessWidget { decoration: decoration ?? TextDecoration.none, fontStyle: fontStyle, color: color, - fontFamily: Theme.of(context).fontFamily, ); if (resetHeight) thisStyle = thisStyle.copyWith(height: 1); @@ -744,6 +743,7 @@ class ZetaText extends StatelessWidget { this.maxWidth, super.key, }) : style = zetaDisplayLarge; + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); diff --git a/lib/src/theme/breakpoints.dart b/lib/src/theme/breakpoints.dart index 9c981666..72c26deb 100644 --- a/lib/src/theme/breakpoints.dart +++ b/lib/src/theme/breakpoints.dart @@ -31,20 +31,7 @@ extension BreakpointLocal on BoxConstraints { /// /// Returns based on the constrains locally to the widget, rather than the whole screen. DeviceType get deviceType { - final width = maxWidth; - if (width <= _Breakpoints._mobilePortraitMax) { - return DeviceType.mobilePortrait; - } else if (width <= _Breakpoints._mobileLandscapeMax) { - return DeviceType.mobileLandscape; - } else if (width <= _Breakpoints._tabletMax) { - return DeviceType.tablet; - } else if (width <= _Breakpoints._desktopMax) { - return DeviceType.desktop; - } else if (width <= _Breakpoints._desktopLMax) { - return DeviceType.desktopL; - } else { - return DeviceType.desktopXL; - } + return _resolveDeviceType(maxWidth); } } @@ -54,21 +41,23 @@ extension BreakpointFull on BuildContext { /// /// Returns based on the full size of the screen, so can be inaccurate in certain scenarios. DeviceType get deviceType { - final width = MediaQuery.of(this).size.width; + return _resolveDeviceType(MediaQuery.of(this).size.width); + } +} - if (width <= _Breakpoints._mobilePortraitMax) { - return DeviceType.mobilePortrait; - } else if (width <= _Breakpoints._mobileLandscapeMax) { - return DeviceType.mobileLandscape; - } else if (width <= _Breakpoints._tabletMax) { - return DeviceType.tablet; - } else if (width <= _Breakpoints._desktopMax) { - return DeviceType.desktop; - } else if (width <= _Breakpoints._desktopLMax) { - return DeviceType.desktopL; - } else { - return DeviceType.desktopXL; - } +DeviceType _resolveDeviceType(double width) { + if (width <= _Breakpoints._mobilePortraitMax) { + return DeviceType.mobilePortrait; + } else if (width <= _Breakpoints._mobileLandscapeMax) { + return DeviceType.mobileLandscape; + } else if (width <= _Breakpoints._tabletMax) { + return DeviceType.tablet; + } else if (width <= _Breakpoints._desktopMax) { + return DeviceType.desktop; + } else if (width <= _Breakpoints._desktopLMax) { + return DeviceType.desktopL; + } else { + return DeviceType.desktopXL; } } diff --git a/lib/src/theme/color_extensions.dart b/lib/src/theme/color_extensions.dart new file mode 100644 index 00000000..d34f6635 --- /dev/null +++ b/lib/src/theme/color_extensions.dart @@ -0,0 +1,305 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import 'color_swatch.dart'; +import 'colors_base.dart'; +import 'constants.dart'; +import 'contrast.dart'; + +/// Extensions on [Color] to brighten, lighten, darken and blend colors and +/// can get a shade for gradients. +/// +/// Some of the extensions are rewrites of TinyColor's functions +/// https://pub.dev/packages/tinycolor. The TinyColor algorithms have also +/// been modified to use Flutter's HSLColor class instead of the custom one in +/// the TinyColor lib. The functions from TinyColor re-implemented as Color +/// extensions here are [brighten], [lighten] and [darken]. They are used +/// for color calculations in ZetaColorScheme, but also exposed for reuse. +/// +/// Another frequently used extension is [blend] and [blendAlpha] used to blend +/// two colors using alpha as a percentage or as an 8-bit int alpha value. +/// This extension is used to calculate branded surface +/// colors used by ZetaColorScheme's branded surfaces and for automatic dark +/// color schemes from a light scheme. +/// +/// The [getShadeColor] extension is less frequently used and when used, +/// typically used to color makes colors shades for gradient AppBars, with +/// default setting to not change black and white. +extension ZetaColorExtensions on Color { + /// {@macro zeta.color.color_to_swatch} + ZetaColorSwatch get zetaColorSwatch => ZetaColorSwatch.fromColor(this); + + /// Brightens the color with the given integer percentage amount. + /// Defaults to 10%. + Color brighten([int amount = 10]) { + if (amount <= 0) return this; + if (amount > 100) return Colors.white; + final Color color = Color.fromARGB( + alpha, + math.max(0, math.min(255, red - (255 * -(amount / 100)).round())), + math.max(0, math.min(255, green - (255 * -(amount / 100)).round())), + math.max(0, math.min(255, blue - (255 * -(amount / 100)).round())), + ); + return color; + } + + /// Lightens the color with the given integer percentage amount. + /// Defaults to 10%. + Color lighten([int amount = 10]) { + if (amount <= 0) return this; + if (amount > 100) return Colors.white; + // HSLColor returns saturation 1 for black, we want 0 instead to be able + // lighten black color up along the grey scale from black. + final HSLColor hsl = + this == const Color(0xFF000000) ? HSLColor.fromColor(this).withSaturation(0) : HSLColor.fromColor(this); + return hsl.withLightness(math.min(1, math.max(0, hsl.lightness + amount / 100))).toColor(); + } + + /// Darkens the color with the given integer percentage amount. + /// Defaults to 10%. + Color darken([int amount = 10]) { + if (amount <= 0) return this; + if (amount > 100) return Colors.black; + final HSLColor hsl = HSLColor.fromColor(this); + return hsl.withLightness(math.min(1, math.max(0, hsl.lightness - amount / 100))).toColor(); + } + + /// This getter returns appropriate contrast color based on a given color. + /// It will return a color chosen according to the brightness of this color. + /// + /// * The [Color] instance on which this getter is called is used to determine the brightness based on [ThemeData.estimateBrightnessForColor] method. + /// * If the estimated brightness is light, it will return a color [ZetaColorBase.greyCool].shade90. + /// * If the estimated brightness is not light (meaning it's dark), it will return [ZetaColorBase.white]. + Color get onColor => isLight ? ZetaColorBase.greyCool.shade90 : ZetaColorBase.white; + + /// Returns true if the color's brightness is [Brightness.light], else false. + bool get isLight => ThemeData.estimateBrightnessForColor(this) == Brightness.light; + + /// Returns true if the color's brightness is [Brightness.dark], else false. + bool get isDark => ThemeData.estimateBrightnessForColor(this) == Brightness.dark; + + /// Blend in the given input Color with a percentage of alpha. + /// + /// You typically apply this on a background color, light or dark + /// to create a background color with a hint of a color used in a theme. + /// + /// This is a use case of the alphaBlend static function that exists in + /// dart:ui Color. It is used to create the branded surface colors in + /// ZetaColorScheme and to calculate dark scheme colors from light ones, + /// by blending in white color with light scheme color. + /// + /// Defaults to 10% alpha blend of the passed in Color value. + Color blend(Color input, [int amount = 10]) { + // Skip blending for impossible value and return the instance color value. + if (amount <= 0) return this; + // Blend amounts >= 100 results in the input Color. + if (amount >= 100) return input; + return Color.alphaBlend(input.withAlpha(255 * amount ~/ 100), this); + } + + /// Blend in the given input Color with an alpha value. + /// + /// You typically apply this on a background color, light or dark + /// to create a background color with a hint of a color used in a theme. + /// + /// This is a use case of the alphaBlend static function that exists in + /// dart:ui Color. It is used to create the branded surface colors in + /// ZetaColorScheme and to calculate dark scheme colors from light ones, + /// by blending in white color with light scheme color. + /// + /// Defaults to alpha 0x0A alpha blend of the passed in Color value, + /// which is 10% alpha blend. + Color blendAlpha(Color input, [int alpha = 0x0A]) { + // Skip blending for impossible value and return the instance color value. + if (alpha <= 0) return this; + // Blend amounts >= 255 results in the input Color. + if (alpha >= 255) return input; + return Color.alphaBlend(input.withAlpha(alpha), this); + } + + /// The [getShadeColor] extension is used to make a color darker or lighter, + /// the [shadeValue] defines the amount in % that the shade should be changed. + /// + /// It can be used to make a shade of a color to be used in a gradient. + /// By default it makes a color that is 15% lighter. If lighten is false + /// it makes a color that is 15% darker by default. + /// + /// By default it does not affect black and white colors, but + /// if [keepWhite] is set to false, it will darken white color when [lighten] + /// is false and return a grey color. Wise versa for black with [keepBlack] + /// set to false, it will lighten black color, when [lighten] is true and + /// return a grey shade. + /// + /// White cannot be made lighter and black cannot be made + /// darker, the extension just returns white or black for such attempts, with + /// a quick exist from the call. + Color getShadeColor({ + int shadeValue = 15, + bool lighten = true, + bool keepBlack = true, + bool keepWhite = true, + }) { + if (shadeValue <= 0) return this; + int usedShadeValue = shadeValue; + if (usedShadeValue > 100) usedShadeValue = 100; + + // Trying to make black darker, just return black + if (this == Colors.black && !lighten) return this; + // Black is defined to be kept as black. + if (this == Colors.black && keepBlack) return this; + // Make black lighter as lighten was set and we do not keepBlack + if (this == Colors.black) return this.lighten(usedShadeValue); + + // Trying to make white lighter, just return white + if (this == Colors.white && lighten) return this; + // White is defined to be kept as white. + if (this == Colors.white && keepWhite) return this; + // Make white darker as we do not keep white. + if (this == Colors.white) return darken(usedShadeValue); + // We are dealing with some other color than white or black, so we + // make it lighter or darker based on flag and requested shade % + if (lighten) { + return this.lighten(usedShadeValue); + } else { + return darken(usedShadeValue); + } + } + + /// Return uppercase Flutter style hex code string of the color. + String get hexCode { + return value.toRadixString(16).toUpperCase().padLeft(8, '0'); + } + + /// Applies lightness percentage to color. + Color withLightness(double percentage) { + final HSLColor hslColor = HSLColor.fromColor(this); + + return hslColor.withLightness(percentage).toColor(); + } + + /// Gets lightness of color. + double get lightness => HSLColor.fromColor(this).lightness; + + /// Calculates the contrast ratio between the current color and the given color [b]. + /// + /// The contrast ratio is computed based on the relative luminance of the two colors + /// and is useful for ensuring readability and accessibility of text on backgrounds. + /// + /// Returns the computed contrast ratio. + double contrastRatio(Color b) { + final luminanceA = computeLuminance(); + final luminanceB = b.computeLuminance(); + if (luminanceA > luminanceB) { + return (luminanceA + 0.05) / (luminanceB + 0.05); + } + return (luminanceB + 0.05) / (luminanceA + 0.05); + } + + /// Adjusts the color to a specific contrast target. + /// + /// Parameters: + /// - [target] The target contrast for the color adjustment. + /// - [on] The color to compare contrast against. Defaults to [Colors.white]. + /// - [maxIterations] The maximum number of iterations for color adjustment. Defaults to 1000 iterations. + /// - [epsilon] The difference tolerance for the contrast ratio. Defaults to 0.1. + /// + /// Returns: + /// - A new color with adjusted contrast close to the target contrast. + Color adjustContrast({ + required Color on, + required double target, + int maxIterations = 1000, + double epsilon = 0.1, + }) { + int iteration = 0; + Color adjustedColor = this; + double adjustmentValue; + + while (iteration < maxIterations) { + final currentContrast = adjustedColor.contrastRatio(on); + + if (currentContrast == target || (currentContrast - target).abs() < epsilon) { + break; + } + + final hslColor = HSLColor.fromColor(adjustedColor); + adjustmentValue = (currentContrast - target).abs() * 0.02; + + var newLightness = + (currentContrast < target) ? hslColor.lightness - adjustmentValue : hslColor.lightness + adjustmentValue; + + newLightness = newLightness.clamp(0.0, 1.0); + + adjustedColor = hslColor.withLightness(newLightness).toColor(); + iteration++; + } + + return adjustedColor; + } + + /// Generates a color swatch for this color. + /// A color swatch is a map with integer keys indexing to [Color] objects, + /// typically used for design themes. + /// + /// The function has optional parameters: + /// + /// * [primary] (Default = [kZetaSwatchPrimaryIndex]) - The primary color index for the swatch. This number should be a key in the swatch map. + /// * [targetContrasts] (Default = [kZetaSwatchTargetContrasts]) - Map of target contrast values for each color index. + /// * [background] (Default = [ZetaColorBase.white]) - The color used to determine the contrast of the colors in the swatch. Generally, this should be the background color that the color swatch will be displayed on. + /// * [adjustPrimary] (Default = true) - Determines whether to adjust the contrast of the primary color on the background color. Useful in cases the brand color is being used. + /// + /// Returns a Map object. + Map generateSwatch({ + int primary = kZetaSwatchPrimaryIndex, + Map targetContrasts = kZetaSwatchTargetContrasts, + Color background = ZetaColorBase.white, + bool adjustPrimary = true, + }) { + assert( + targetContrasts.containsKey(primary), + 'Primary key not found in targetContrasts.', + ); + + assert( + targetContrasts.values.every((v) => v > 0), + 'All values in targetContrasts should be positive and non-null.', + ); + + final swatch = {}; + + final adjustedPrimary = adjustPrimary + ? adjustContrast( + on: background, + target: targetContrasts[primary]!, + ) + : this; + + swatch[primary] = adjustedPrimary; + + for (final shade in targetContrasts.keys) { + if (shade != primary) { + swatch[shade] = adjustedPrimary.adjustContrast( + on: background, + target: targetContrasts[shade]!, + ); + } + } + + return swatch; + } + + /// Adjusts the current color to meet the specified accessibility standard [standard] when set against the [on] color. + /// + /// By default, the AA accessibility standard is targeted. You can optionally target AAA. + /// AA: The contrast ratio should be at least 4.57:1 + /// AAA: The contrast ratio should be at least 8.33:1 + /// + /// Returns the adjusted color that meets the specified accessibility standard. + /// Adjusts the current color to meet a specified accessibility standard [standard] when set against the [on] color. + Color ensureAccessibility({ + required Color on, + ZetaContrast standard = ZetaContrast.aa, + }) { + return adjustContrast(on: on, target: standard.targetContrast); + } +} diff --git a/lib/src/theme/color_scheme.dart b/lib/src/theme/color_scheme.dart new file mode 100644 index 00000000..ff6d4a92 --- /dev/null +++ b/lib/src/theme/color_scheme.dart @@ -0,0 +1,170 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'colors.dart'; +import 'constants.dart'; + +/// Easily craft stunning Flutter themes using pre-set color patterns or your own +/// choices. +/// +/// While Flutter's [ThemeData.from] offers a starting point for [ColorScheme]-based +/// themes, it sometimes defaults to the standard [ThemeData] factory, leading to inconsistencies, +/// particularly in dark themes. [ZetaColorScheme] addresses this by ensuring a seamless theme creation +/// with the [ColorScheme] approach. +/// +/// Themes can be formed using a default [ColorScheme], or by inputting select color values. If both a +/// [ColorScheme] and individual colors are provided, the individual colors will prevail. +/// +/// These factory constructors enable the creation of color-toned themes from a singular color. Additionally, +/// [ZetaColorScheme] simplifies theme crafting with color-branded backgrounds, blending different color degrees, primarily +/// the primary color. +/// +/// Adjusting the themed background of the [AppBar] is straightforward with [ZetaColorScheme], ensuring it aligns with themed colors. +@immutable +class ZetaColorScheme extends ColorScheme with Diagnosticable { + /// Default constructor with no required properties. + /// + /// Creates a light theme by default using the M2 colors as its default + /// theme. + const ZetaColorScheme({ + required this.zetaColors, + + /// If null, defaults to [kZetaFontFamily]; + this.fontFamily = kZetaFontFamily, + required super.brightness, + required super.background, + required super.error, + required super.onBackground, + required super.onError, + required super.onPrimary, + required super.onSecondary, + required super.onSurface, + required super.primary, + required super.secondary, + required super.surface, + super.errorContainer, + super.inversePrimary, + super.inverseSurface, + super.onErrorContainer, + super.onInverseSurface, + super.onPrimaryContainer, + super.onSecondaryContainer, + super.onSurfaceVariant, + super.onTertiary, + super.onTertiaryContainer, + super.outline, + super.outlineVariant, + super.primaryContainer, + super.scrim, + super.secondaryContainer, + super.shadow, + super.surfaceTint, + super.surfaceVariant, + super.tertiary, + super.tertiaryContainer, + }); + + /// Current instance of the [ZetaColors] + final ZetaColors zetaColors; + + /// Name of the font family to use as default font for the text theme in + /// created theme. + /// + /// Same feature as in [ThemeData] factory. Used to apply the font family + /// name to default text theme and primary text theme, also passed along + /// to [ThemeData], + final String? fontFamily; + + /// Creates the copy of the current scheme from the provided values. + @override + ZetaColorScheme copyWith({ + ZetaColors? zetaColors, + String? fontFamily, + Brightness? brightness, + Color? primary, + Color? onPrimary, + Color? primaryContainer, + Color? onPrimaryContainer, + Color? secondary, + Color? onSecondary, + Color? secondaryContainer, + Color? onSecondaryContainer, + Color? tertiary, + Color? onTertiary, + Color? tertiaryContainer, + Color? onTertiaryContainer, + Color? error, + Color? onError, + Color? errorContainer, + Color? onErrorContainer, + Color? background, + Color? onBackground, + Color? surface, + Color? onSurface, + Color? surfaceVariant, + Color? onSurfaceVariant, + Color? outline, + Color? outlineVariant, + Color? shadow, + Color? scrim, + Color? inverseSurface, + Color? onInverseSurface, + Color? inversePrimary, + Color? surfaceTint, + }) { + return ZetaColorScheme( + zetaColors: zetaColors ?? this.zetaColors, + fontFamily: fontFamily ?? this.fontFamily, + brightness: brightness ?? this.brightness, + primary: primary ?? this.primary, + onPrimary: onPrimary ?? this.onPrimary, + primaryContainer: primaryContainer ?? this.primaryContainer, + onPrimaryContainer: onPrimaryContainer ?? this.onPrimaryContainer, + secondary: secondary ?? this.secondary, + onSecondary: onSecondary ?? this.onSecondary, + secondaryContainer: secondaryContainer ?? this.secondaryContainer, + onSecondaryContainer: onSecondaryContainer ?? this.onSecondaryContainer, + tertiary: tertiary ?? this.tertiary, + onTertiary: onTertiary ?? this.onTertiary, + tertiaryContainer: tertiaryContainer ?? this.tertiaryContainer, + onTertiaryContainer: onTertiaryContainer ?? this.onTertiaryContainer, + error: error ?? this.error, + onError: onError ?? this.onError, + errorContainer: errorContainer ?? this.errorContainer, + onErrorContainer: onErrorContainer ?? this.onErrorContainer, + background: background ?? this.background, + onBackground: onBackground ?? this.onBackground, + surface: surface ?? this.surface, + onSurface: onSurface ?? this.onSurface, + surfaceVariant: surfaceVariant ?? this.surfaceVariant, + onSurfaceVariant: onSurfaceVariant ?? this.onSurfaceVariant, + outline: outline ?? this.outline, + outlineVariant: outlineVariant ?? this.outlineVariant, + shadow: shadow ?? this.shadow, + scrim: scrim ?? this.scrim, + inverseSurface: inverseSurface ?? this.inverseSurface, + onInverseSurface: onInverseSurface ?? this.onInverseSurface, + inversePrimary: inversePrimary ?? this.inversePrimary, + surfaceTint: surfaceTint ?? this.surfaceTint, + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('zetaColors', zetaColors)); + properties.add(StringProperty('fontFamily', fontFamily)); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + super == other && + other is ZetaColorScheme && + runtimeType == other.runtimeType && + zetaColors == other.zetaColors && + fontFamily == other.fontFamily; + + @override + int get hashCode => super.hashCode ^ zetaColors.hashCode ^ fontFamily.hashCode; +} diff --git a/lib/src/theme/color_swatch.dart b/lib/src/theme/color_swatch.dart new file mode 100644 index 00000000..8401cca8 --- /dev/null +++ b/lib/src/theme/color_swatch.dart @@ -0,0 +1,197 @@ +import 'package:flutter/material.dart'; + +import 'color_extensions.dart'; +import 'colors_base.dart'; +import 'contrast.dart'; + +/// A swatch of colors with values from 10 (light) to 100 (dark). +@immutable +class ZetaColorSwatch extends ColorSwatch { + /// Selected contrast level of the system + final Brightness brightness; + + /// Selected contrast level of the system + final ZetaContrast contrast; + + /// Constructs a [ZetaColorSwatch]. + /// + /// See also: + /// * [MaterialColor]. + const ZetaColorSwatch({ + this.brightness = Brightness.light, + this.contrast = ZetaContrast.aa, + required int primary, + required Map swatch, + }) : super(primary, swatch); + + /// This method is an override of the index operator. + /// + /// If the requested index is not in the table (i.e., it results in `null`), the method returns `this`, + /// presumably the default color. + /// + /// [index] The index of the color swatch to return. Must be a non-negative integer. + /// + /// Returns the color at the specified swatch index, or the default color if the index is not in the table. + @override + Color? operator [](int index) => super[brightness == Brightness.dark ? 110 - index : index] ?? this; + + /// The lightest shade. + Color get shade10 => this[10]!; + + /// The second lightest shade. + Color get shade20 => this[20]!; + + /// The third lightest shade. + Color get shade30 => this[30]!; + + /// The fourth lightest shade. + Color get shade40 => this[40]!; + + /// The fifth lightest shade. + Color get shade50 => this[50]!; + + /// The default shade. + Color get shade60 => this[60]!; + + /// The fourth darkest shade. + Color get shade70 => this[70]!; + + /// The third darkest shade. + Color get shade80 => this[80]!; + + /// The second darkest shade. + Color get shade90 => this[90]!; + + /// The darkest shade. + Color get shade100 => this[100]!; + + /// Takes an integer as argument and returns a color shade based on that number. + /// If no shade with the given number exists, it returns the color itself. + /// + /// [number] - The number representing the shade of the color. + /// Returns a color object that represents a specified shade or the color itself. + /// + Color shade(int number) => this[number]!; + + /// {@template zeta.color.color_to_swatch} + /// `ZetaColorSwatch` is a color swatch utility to produce different shades + /// of a primary color, following a specific progression of lightness and darkness. + /// + /// This factory constructor creates a color swatch based on a provided primary color. + /// The darker and lighter shades are determined by predefined percentage values. + /// + /// It ensures that the 60th and 80th shades from swatch are abide by the AA and AAA accessibility standards on [background], respectively. + /// [background] color defaults to [ZetaColorBase.greyWarm] shade10. + factory ZetaColorSwatch.fromColor( + Color primary, { + Brightness brightness = Brightness.light, + ZetaContrast contrast = ZetaContrast.aa, + Color background = Colors.white, + }) { + /// Returns a map of colors shades with their respective indexes. + /// Darker shades are obtained by darkening the primary color and + /// lighter shades by lightening it. + /// + /// - 100, 90, 80, and 70 are darker shades of the primary color. + /// - 60 is the primary color itself. + /// - 50, 40, 30, 20, and 10 are progressively lighter shades of the primary color. + return ZetaColorSwatch( + contrast: contrast, + brightness: brightness, + primary: primary.value, + swatch: primary.generateSwatch(background: background), + ).apply(brightness: brightness); + } + + /// Returns the color shade for a surface depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 60. + /// For [ZetaContrast.aaa], it returns 80. + Color get text => shade(contrast.text); + + /// Returns the color shade for an icon depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 60. + /// For [ZetaContrast.aaa], it returns 80. + Color get icon => shade(contrast.icon); + + /// Returns the color shade for a hover state depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 70. + /// For [ZetaContrast.aaa], it returns 90. + Color get hover => shade(contrast.hover); + + /// Returns the color shade for a selected state depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 80. + /// For [ZetaContrast.aaa], it returns 100. + Color get selected => shade(contrast.selected); + + /// Returns the color shade for a focus state depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 80. + /// For [ZetaContrast.aaa], it returns 100. + Color get focus => shade(contrast.focus); + + /// Returns the color shade for a border depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 60. + /// For [ZetaContrast.aaa], it returns 80. + Color get border => shade(contrast.border); + + /// Returns the color shade for a subtle visual element depending on the ZetaContrast value. + /// + /// For both [ZetaContrast.aa] and [ZetaContrast.aaa], it returns 40. + Color get subtle => shade(contrast.subtle); + + /// Returns the color shade for a surface depending on the ZetaContrast value. + /// + /// For both [ZetaContrast.aa] and [ZetaContrast.aaa], it returns 10. + Color get surface => shade(contrast.surface); + + /// Creates a copy of the current [ZetaColorSwatch] with potential modifications + /// based on the provided [contrast] and [brightness]. + /// + /// The [contrast] determines which shade of the color should be used + /// as the primary color in the copied swatch. + /// + /// - [contrast] : The shade to use as the primary color in the new swatch. + /// Defaults to [ZetaContrast.aa]. + /// - [brightness] : The brightness value for the new swatch. + /// Defaults to [Brightness.light]. + ZetaColorSwatch apply({ + ZetaContrast contrast = ZetaContrast.aa, + Brightness brightness = Brightness.light, + }) { + if (this.contrast == contrast && this.brightness == brightness) return this; + + // Generate a list of indices based on brightness level + final indices = List.generate(10, (index) => (index + 1) * 10); + + // Create a new map (swatch) based on the indices and current swatch values + final swatch = Map.fromEntries(indices.map((i) => MapEntry(i, super[i] ?? this))); + + // Determine the primaryIndex color of the new swatch based on the accessibility level + final primaryIndex = brightness == Brightness.light ? contrast.primary : 110 - contrast.primary; + + // Return a new ZetaColorSwatch object with the new primaryIndex color and swatch + return ZetaColorSwatch( + contrast: contrast, + brightness: brightness, + primary: swatch[primaryIndex]!.value, + swatch: swatch, + ); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + super == other && + other is ZetaColorSwatch && + runtimeType == other.runtimeType && + brightness == other.brightness && + contrast == other.contrast; + + @override + int get hashCode => super.hashCode ^ brightness.hashCode ^ contrast.hashCode; +} diff --git a/lib/src/theme/colors.dart b/lib/src/theme/colors.dart index 198d8a22..fb9b5b20 100644 --- a/lib/src/theme/colors.dart +++ b/lib/src/theme/colors.dart @@ -1,22 +1,57 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; -import '../../../zeta_flutter.dart'; +import 'color_extensions.dart'; +import 'color_scheme.dart'; +import 'color_swatch.dart'; +import 'colors_base.dart'; +import 'contrast.dart'; /// Zeta Colors. /// /// A customizable, token-based color palette, adapting Zeta colors to Flutter's colorScheme. +@immutable class ZetaColors { - /// Sets dark mode to colors. + /// Represents the brightness value. + final Brightness brightness; + + /// Represents the Zeta accessibility standard. + /// {@macro zeta-color-dark} + final ZetaContrast contrast; + + /// Primary color swatch. + /// + /// Defaults to [ZetaColorBase.blue]. + /// + /// {@macro zeta-color-dark} + final ZetaColorSwatch primary; + + /// Secondary color used in app. + /// + /// Defaults to `ZetaColors.cool.90`. + /// + /// Maps to [ColorScheme.secondary]. + final ZetaColorSwatch secondary; + + /// Secondary color used in app. + /// + /// Defaults to `ZetaColors.red.60`. + /// + /// Maps to [ColorScheme.error]. + final ZetaColorSwatch error; + + /// Cool grey color swatch. + /// + /// Defaults to [ZetaColorBase.greyCool]. /// - /// If using a default theme, this sets all the parameters needed, otherwise dark colors must be provided. - final bool isDarkMode; + /// {@macro zeta-color-dark} + final ZetaColorSwatch cool; - /// Sets whether colors should meet AAA requirements for accessability when using built in colors. + /// Warm grey color swatch. /// - /// Typically, when true, default color shade is 80, rather than 60. - final bool isAAA; + /// Defaults to [ZetaColorBase.greyWarm]. + /// + /// {@macro zeta-color-dark} + final ZetaColorSwatch warm; /// Shadow color. /// @@ -25,80 +60,19 @@ class ZetaColors { /// Defaults to #49505E at 10% opacity. final Color shadow; - final Color _linkLight; - final Color _linkVisitedLight; - final Color _linkDark; - final Color _linkVisitedDark; - - final ZetaColorSwatch _primary; - final ZetaColorSwatch _secondary; - final ZetaColorSwatch _cool; - final ZetaColorSwatch _warm; - final ZetaColorSwatch _blue; - final ZetaColorSwatch _green; - final ZetaColorSwatch _red; - final ZetaColorSwatch _orange; - final ZetaColorSwatch _purple; - final ZetaColorSwatch _yellow; - final ZetaColorSwatch _teal; - final ZetaColorSwatch _pink; - - final Color? _background; - final Color? _surface; - final Color? _white; - final Color? _black; - - final Color? _onPrimary; - final Color? _onSecondary; - final Color? _onNegative; - final Color? _onBackground; - final Color? _onSurface; - - final Map _generatedColors; - - static const _minShade = 10; - - /// Generated color for use on [primary]. - /// - /// {@template zeta-color-on-X} - /// If [secondary] is dark, [onSecondary] will be [textDarkMode]. - /// - /// If [secondary] is light, [onSecondary] will be [textLightMode]. - /// {@endtemplate} - Color get onPrimary { - if (_onPrimary != null) return _onPrimary!; - // if (_generatedColors['onPrimary'] != null) return _generatedColors['onPrimary']!; - _generatedColors['onPrimary'] = computeForegroundFromTheme(input: primary); - - return _generatedColors['onPrimary']!; - } - - /// Generated color for use on [secondary]. + /// White color. /// - /// {@macro zeta-color-on-X} - Color get onSecondary { - if (_onSecondary != null) return _onSecondary!; - if (_generatedColors['onSecondary'] != null) return _generatedColors['onSecondary']!; - _generatedColors['onSecondary'] = computeForegroundFromTheme(input: secondary); - - return _generatedColors['onSecondary']!; - } - - /// Generated color for use on [negative]. + /// Maps to [ColorScheme.surface]. /// - /// {@macro zeta-color-on-X} - Color get onNegative { - if (_onNegative != null) return _onNegative!; - if (_generatedColors['onNegative'] != null) return _generatedColors['onNegative']!; - _generatedColors['onNegative'] = computeForegroundFromTheme(input: negative); - - return _generatedColors['onNegative']!; - } + /// Defaults to [ZetaColorBase.white]. + final Color white; - /// On surface color. + /// Shadow color. + /// + /// Maps to [ColorScheme.surface]. /// - /// {@macro zeta-color-on-X} - Color get onSurface => _onSurface ?? textDefault; + /// Defaults to [ZetaColorBase.black]. + final Color black; // Text / icons. @@ -107,7 +81,7 @@ class ZetaColors { /// Defaults to `ZetaColors.cool.90`. /// /// {@template zeta-color-dark} - /// Color swatches are inverted if [ZetaColors.isDarkMode]. + /// Color swatches are inverted if [ZetaColors.brightness] is Dark. /// {@endTemplate} Color get textDefault => cool.shade90; @@ -129,18 +103,52 @@ class ZetaColors { /// Inverse text / icon color. /// - /// Used for text that is not on [ColorScheme.background] or [ColorScheme.surface]. + /// Used for text that is not on [ColorScheme.background] or [ThemeData.scaffoldBackgroundColor]. /// /// Defaults to `ZetaColors.cool.20`. /// /// {@macro zeta-color-dark} Color get textInverse => cool.shade20; + /// Default icon color. + /// + /// Defaults to `ZetaColors.cool.90`. + /// + /// {@template zeta-color-dark} + /// Color swatches are inverted if [ZetaColors.brightness] is Dark. + /// {@endTemplate} + Color get iconDefault => textDefault; + + /// Subtle icon color. + /// + /// Defaults to `ZetaColors.cool.70`. + /// + /// Maps to [ColorScheme.onBackground]. + /// + /// {@macro zeta-color-dark} + Color get iconSubtle => textSubtle; + + /// Disabled icon color. + /// + /// Defaults to `ZetaColors.cool.50`. + /// + /// {@macro zeta-color-dark} + Color get iconDisabled => textDisabled; + + /// Inverse icon color. + /// + /// Used for text that is not on [ColorScheme.background] or [ThemeData.scaffoldBackgroundColor]. + /// + /// Defaults to `ZetaColors.cool.20`. + /// + /// {@macro zeta-color-dark} + Color get iconInverse => textInverse; + // Border variants. /// Default border color. /// - /// Defaults to `ZetaColors.warm.50`. + /// Defaults to `ZetaColors.cool.50`. /// /// {@macro zeta-color-dark} Color get borderDefault => cool.shade50; @@ -166,30 +174,40 @@ class ZetaColors { /// {@macro zeta-color-dark} Color get borderSelected => cool.shade90; + // Links + + /// Link color. + /// + /// Defaults to [ZetaColorBase.linkLight] or [ZetaColorBase.linkDark]. + final Color link; + + /// Link color. + /// + /// Defaults to [ZetaColorBase.linkVisitedLight] or [ZetaColorBase.linkVisitedDark]. + final Color linkVisited; + // Backdrop colors. /// Surface color. /// - /// Maps to [ColorScheme.background]. + /// Maps to [ColorScheme.surface]. /// - /// * Light mode: `ZetaColors.cool.10` + /// * Light mode: `ZetaColors.black` /// * Dark mode: `ZetaColors.white`. - Color get surfacePrimary => isDarkMode ? cool.shade10 : ZetaColorBase.white; + final Color surfacePrimary; - /// Background color. + /// Secondary surface color. /// - /// See [ColorScheme.background]. - Color get background => _background ?? warm.shade10; - - /// On background color. /// - /// See [ColorScheme.onBackground]. - Color get onBackground => _onBackground ?? textSubtle; + /// * `ZetaColors.cool.10`. + final Color surfaceSecondary; - /// Surface color. + /// Tertiary surface color. + /// + /// Maps to [ColorScheme.background] and [ThemeData.scaffoldBackgroundColor] /// - /// See [ColorScheme.surface]. - Color get surface => _surface ?? surfaceSecondary; + /// * `ZetaColors.warm.10`. + final Color surfaceTertiary; /// Disabled surface color. /// @@ -205,634 +223,784 @@ class ZetaColors { /// {@macro zeta-color-dark} Color get surfaceHovered => cool.shade20; - /// Secondary surface color. - /// - /// * Light mode: `ZetaColors.cool.10`. - /// * Dark mode: `ZetaColors.warm.30`. - Color get surfaceSecondary => isDarkMode ? cool.shade10 : white; - - /// Tertiary surface color. - /// - /// Maps to [ColorScheme.background]. - /// - /// * Light mode: `ZetaColors.warm.10`. - /// * Dark mode: `ZetaColors.cool.30`. - Color get surfaceTertiary => isDarkMode ? cool.shade30 : warm.shade10; - /// Selected hover surface color. /// /// Defaults to: `ZetaColors.blue.20`. - Color get surfaceSelectedHovered => blue.shade20; + Color get surfaceSelectedHovered => primary.shade20; /// Selected surface color. /// /// Defaults to: `ZetaColors.blue.10`. - Color get surfaceSelected => blue.shade10; + Color get surfaceSelected => primary.shade10; + + // Alert Colors /// Green positive color. /// - /// Defaults to `ZetaColors.green.60`. + /// Defaults to `ZetaColors.green.60` in AA system. + /// Defaults to `ZetaColors.green.80` in AAA system. /// /// {@macro zeta-color-dark} /// /// {@macro zeta-color-aaa} - Color get positive => green.shade60; + ZetaColorSwatch get positive => green; /// Red negative color. /// - /// Defaults to `ZetaColors.red.60`. + /// Defaults to `ZetaColors.red.60` in AA system. + /// Defaults to `ZetaColors.red.80` in AAA system. /// /// Maps to [ColorScheme.error]. /// /// {@macro zeta-color-dark} /// /// {@macro zeta-color-aaa} - Color get negative => red.shade60; + ZetaColorSwatch get negative => error; /// Orange warning color. /// - /// Defaults to `ZetaColors.orange.60`. + /// Defaults to `ZetaColors.orange.60` in AA system. + /// Defaults to `ZetaColors.orange.80` in AAA system. /// /// {@macro zeta-color-dark} /// /// {@macro zeta-color-aaa} - Color get warning => orange.primary; + ZetaColorSwatch get warning => orange; /// Purple info color. /// - /// Defaults to `ZetaColors.purple.60`. + /// Defaults to `ZetaColors.purple.60` in AA system. + /// Defaults to `ZetaColors.purple.80` in AAA system. /// /// {@macro zeta-color-dark} /// /// {@macro zeta-color-aaa} - Color get info => purple.primary; - - /// Pure white color. - /// - /// `ZetaColors.white`. - Color get white => _white ?? ZetaColorBase.white; - - /// Pure black color. - /// - /// `ZetaColors.black`. - Color get black => _black ?? ZetaColorBase.black; - - /// Link color. - /// - /// Defaults to [ZetaColorBase.linkLight] or [ZetaColorBase.linkDark]. - Color get link => isDarkMode ? _linkDark : _linkLight; - - /// Link color. - /// - /// Defaults to [ZetaColorBase.linkVisitedLight] or [ZetaColorBase.linkVisitedDark]. - Color get linkVisited => isDarkMode ? _linkVisitedDark : _linkVisitedLight; - - /// Static text color for light mode. Does not change based on [isDarkMode]. - /// - /// Defaults to `ZetaColors.cool.90`. - Color get textLightMode => ZetaColorBase.greyCool.shade90; - - /// Static text color for dark mode. Does not change based on [isDarkMode]. - /// - /// Defaults to `ZetaColors.cool.20`. - Color get textDarkMode => ZetaColorBase.greyCool.shade20; - - /// Primary color swatch. - /// - /// Defaults to [ZetaColorBase.blue]. - /// - /// {@macro zeta-color-dark} - ZetaColorSwatch get primary => _primary.copyWith(isDarkMode: isDarkMode, isAAA: isAAA); - - /// Secondary color used in app. - /// - /// Defaults to `ZetaColors.cool.90`. - /// - /// Maps to [ColorScheme.secondary]. - ZetaColorSwatch get secondary => _secondary.copyWith(isDarkMode: isDarkMode, isAAA: isAAA); - - /// Cool grey color swatch. - /// - /// Defaults to [ZetaColorBase.greyCool]. - /// - /// {@macro zeta-color-dark} - ZetaColorSwatch get cool => _cool.copyWith(isAAA: isAAA, isDarkMode: isDarkMode); - - /// Warm grey color swatch. - /// - /// Defaults to [ZetaColorBase.greyWarm]. - /// - /// {@macro zeta-color-dark} - ZetaColorSwatch get warm => _warm.copyWith(isAAA: isAAA, isDarkMode: isDarkMode); + ZetaColorSwatch get info => purple; /// Blue color swatch. /// /// Defaults to [ZetaColorBase.blue]. /// /// {@macro zeta-color-dark} - ZetaColorSwatch get blue => _blue.copyWith(isAAA: isAAA, isDarkMode: isDarkMode); + final ZetaColorSwatch blue; /// Green color swatch. /// /// Defaults to [ZetaColorBase.green]. /// /// {@macro zeta-color-dark} - ZetaColorSwatch get green => _green.copyWith(isAAA: isAAA, isDarkMode: isDarkMode); + final ZetaColorSwatch green; /// Red color swatch. /// /// Defaults to [ZetaColorBase.red]. /// /// {@macro zeta-color-dark} - ZetaColorSwatch get red => _red.copyWith(isAAA: isAAA, isDarkMode: isDarkMode); + final ZetaColorSwatch red; /// Orange color swatch. /// /// Defaults to [ZetaColorBase.orange]. /// /// {@macro zeta-color-dark} - ZetaColorSwatch get orange => _orange.copyWith(isAAA: isAAA, isDarkMode: isDarkMode); + final ZetaColorSwatch orange; /// Purple color swatch. /// /// Defaults to [ZetaColorBase.purple]. /// /// {@macro zeta-color-dark} - ZetaColorSwatch get purple => _purple.copyWith(isAAA: isAAA, isDarkMode: isDarkMode); + final ZetaColorSwatch purple; /// Yellow color swatch. /// /// Defaults to [ZetaColorBase.yellow]. /// /// {@macro zeta-color-dark} - ZetaColorSwatch get yellow => _yellow.copyWith(isAAA: isAAA, isDarkMode: isDarkMode); + final ZetaColorSwatch yellow; /// Teal color swatch. /// /// Defaults to [ZetaColorBase.teal]. /// /// {@macro zeta-color-dark} - ZetaColorSwatch get teal => _teal.copyWith(isAAA: isAAA, isDarkMode: isDarkMode); + final ZetaColorSwatch teal; /// Pink color swatch. /// /// Defaults to [ZetaColorBase.pink]. /// /// {@macro zeta-color-dark} - ZetaColorSwatch get pink => _pink.copyWith(isAAA: isAAA, isDarkMode: isDarkMode); - - /// Generates a [ColorScheme] based on current instance of [ZetaColors]. - /// - /// Colors correspond to the following values: - /// - /// * [ColorScheme.brightness] : [isDarkMode]. - /// * [ColorScheme.primary] : [ZetaColors.primary]. - /// * [ColorScheme.onPrimary] : [onPrimary]. - /// * [ColorScheme.secondary] : [ZetaColors.secondary]. - /// * [ColorScheme.error] : [negative]. - /// * [ColorScheme.onError] : [ZetaColorBase.white]. - /// * [ColorScheme.background] : [surfaceSecondary]. - /// * [ColorScheme.onBackground] : [textSubtle]. - /// * [ColorScheme.surface] : [surfacePrimary]. - /// * [ColorScheme.onSurface] : [textDefault]. - /// * [ColorScheme.shadow] : [shadow]. - ColorScheme get toColorScheme { - return ColorScheme( - brightness: isDarkMode ? Brightness.dark : Brightness.light, - primary: primary.primary, - onPrimary: onPrimary, - secondary: secondary.primary, - onSecondary: onSecondary, - error: negative, - onError: onNegative, - background: background, - onBackground: onBackground, - surface: surface, - onSurface: onSurface, - shadow: shadow, - - // TODO(colors): Consider adding the following colors: - - // errorContainer: - // onErrorContainer: - // inversePrimary: - // inverseSurface: - // onInverseSurface: - // primaryContainer: - // onPrimaryContainer: - // secondaryContainer: - // onSecondaryContainer: - // surfaceVariant: - // onSurfaceVariant: - // tertiary: - // onTertiary: - // tertiaryContainer: - // onTertiaryContainer: - // outline: - // outlineVariant: - // scrim: - // surfaceTint: - ); - } + final ZetaColorSwatch pink; /// Colorful colors. List get rainbow => [red, orange, yellow, green, blue, teal, pink]; /// Default constructor for instance of [ZetaColors]. ZetaColors({ - ZetaColorSwatch primary = ZetaColorBase.blue, - ZetaColorSwatch secondary = ZetaColorBase.blue, - ZetaColorSwatch cool = ZetaColorBase.greyCool, - ZetaColorSwatch warm = ZetaColorBase.greyWarm, - ZetaColorSwatch blue = ZetaColorBase.blue, - ZetaColorSwatch green = ZetaColorBase.green, - ZetaColorSwatch red = ZetaColorBase.red, - ZetaColorSwatch orange = ZetaColorBase.orange, - ZetaColorSwatch purple = ZetaColorBase.purple, - ZetaColorSwatch yellow = ZetaColorBase.yellow, - ZetaColorSwatch teal = ZetaColorBase.teal, - ZetaColorSwatch pink = ZetaColorBase.pink, - Color linkLight = ZetaColorBase.linkLight, - Color linkVisitedLight = ZetaColorBase.linkVisitedLight, - Color linkDark = ZetaColorBase.linkDark, - Color linkVisitedDark = ZetaColorBase.linkVisitedDark, - this.shadow = ZetaColorBase.shadow, - this.isDarkMode = false, - this.isAAA = false, - Color? onPrimary, - Color? onSecondary, - Color? onNegative, - Color? background, - Color? onBackground, - Color? surface, - Color? onSurface, - Color? black, + this.brightness = Brightness.light, + this.contrast = ZetaContrast.aa, + this.link = ZetaColorBase.linkLight, + this.linkVisited = ZetaColorBase.linkVisitedLight, + this.shadow = ZetaColorBase.shadowLight, + this.white = ZetaColorBase.white, + this.black = ZetaColorBase.black, + ZetaColorSwatch? primary, + ZetaColorSwatch? secondary, + ZetaColorSwatch? error, + ZetaColorSwatch? cool, + ZetaColorSwatch? warm, + Color? surfacePrimary, + Color? surfaceSecondary, + Color? surfaceTertiary, + bool adjust = true, + }) : primary = _adjustedValue(primary, ZetaColorBase.blue, adjust, brightness, contrast), + secondary = _adjustedValue(secondary, primary ?? ZetaColorBase.blue, adjust, brightness, contrast), + error = _adjustedValue(error, ZetaColorBase.red, adjust, brightness, contrast), + cool = _adjustedValue(cool, ZetaColorBase.greyCool, adjust, brightness, ZetaContrast.aa), + warm = _adjustedValue(warm, ZetaColorBase.greyWarm, adjust, brightness, ZetaContrast.aa), + blue = _adjustedBase(ZetaColorBase.blue, adjust, brightness, contrast), + green = _adjustedBase(ZetaColorBase.green, adjust, brightness, contrast), + red = _adjustedBase(ZetaColorBase.red, adjust, brightness, contrast), + orange = _adjustedBase(ZetaColorBase.orange, adjust, brightness, contrast), + purple = _adjustedBase(ZetaColorBase.purple, adjust, brightness, contrast), + yellow = _adjustedBase(ZetaColorBase.yellow, adjust, brightness, contrast), + teal = _adjustedBase(ZetaColorBase.teal, adjust, brightness, contrast), + pink = _adjustedBase(ZetaColorBase.pink, adjust, brightness, contrast), + surfacePrimary = surfacePrimary ?? white, + surfaceSecondary = surfaceSecondary ?? + _adjustedValue( + cool, + ZetaColorBase.greyCool, + adjust, + brightness, + ZetaContrast.aa, + ).shade10, + surfaceTertiary = surfaceTertiary ?? + _adjustedValue( + warm, + ZetaColorBase.greyWarm, + adjust, + brightness, + ZetaContrast.aa, + ).shade10; + + /// Helper function to adjust color swatch values based on brightness and contrast + static ZetaColorSwatch _adjustedValue( + ZetaColorSwatch? value, + ZetaColorSwatch defaultValue, + bool adjust, + Brightness brightness, + ZetaContrast contrast, + ) { + final swatch = value ?? defaultValue; + return adjust ? swatch.apply(brightness: brightness, contrast: contrast) : swatch; + } + + /// Helper function to adjust color base values based on brightness and contrast + static ZetaColorSwatch _adjustedBase( + ZetaColorSwatch baseColor, + bool adjust, + Brightness brightness, + ZetaContrast contrast, + ) { + return adjust ? baseColor.apply(brightness: brightness, contrast: contrast) : baseColor; + } + + /// Factory constructor for a light theme for [ZetaColors]. + /// + /// All color options are nullable and default to a pre-defined contrast color if null. + /// + /// [contrast] The primary contrast color. If not supplied, defaults to [ZetaContrast.aa]. + /// [primary] A color swatch for primary color accent. Defaults to null. + /// [secondary] A color swatch for secondary color accent. Defaults to null. + /// [error] A color swatch for error states. Defaults to null. + /// [cool] A color swatch for cooler color tones. Defaults to null. + /// [warm] A color swatch for warmer color tones. Defaults to null. + /// [white] A color option for white color. Defaults to null. + /// [black] A color option for black color. Defaults to null. + /// [link] A color option for links. Defaults to null. + /// [linkVisited] A color option for visited links. Defaults to null. + /// [shadow] A color option for shadows. Defaults to null. + factory ZetaColors.light({ + ZetaContrast contrast = ZetaContrast.aa, + ZetaColorSwatch? primary, + ZetaColorSwatch? secondary, + ZetaColorSwatch? error, + ZetaColorSwatch? cool, + ZetaColorSwatch? warm, Color? white, - }) : _primary = primary, - _secondary = secondary, - _cool = cool, - _warm = warm, - _blue = blue, - _green = green, - _red = red, - _orange = orange, - _purple = purple, - _yellow = yellow, - _teal = teal, - _pink = pink, - _linkLight = linkLight, - _linkVisitedLight = linkVisitedLight, - _linkDark = linkDark, - _linkVisitedDark = linkVisitedDark, - _onPrimary = onPrimary, - _onSecondary = onSecondary, - _onNegative = onNegative, - _background = background, - _onBackground = onBackground, - _surface = surface, - _onSurface = onSurface, - _black = black, - _white = white, - _generatedColors = {}; - - /// Creates a [ZetaColors] from individual colors and generates their full swatches with [ZetaColorSwatch.fromColor] - ZetaColors.fromColors({ - Color? primary, - Color? secondary, - Color? cool, - Color? warm, - Color? blue, - Color? green, - Color? red, - Color? orange, - Color? purple, - Color? yellow, - Color? teal, - Color? pink, - Color? linkLight, - Color? linkVisitedLight, - Color? linkDark, - Color? linkVisitedDark, - Color? onPrimary, - Color? onSecondary, - Color? onNegative, - Color? background, - Color? onBackground, - Color? surface, - Color? onSurface, Color? black, + Color? link, + Color? linkVisited, + Color? shadow, + }) { + return ZetaColors( + white: white ?? ZetaColorBase.white, + black: black ?? ZetaColorBase.black, + cool: cool, + warm: warm, + error: error, + primary: primary, + contrast: contrast, + secondary: secondary, + surfaceTertiary: warm?.shade10, + surfaceSecondary: cool?.shade10, + surfacePrimary: white ?? ZetaColorBase.white, + link: link ?? ZetaColorBase.linkLight, + shadow: shadow ?? ZetaColorBase.shadowLight, + linkVisited: linkVisited ?? ZetaColorBase.linkVisitedLight, + ); + } + + /// Factory constructor for a dark theme for [ZetaColors]. + /// + /// All color options are nullable and default to a pre-defined contrast color if null. + /// + /// [contrast] The primary contrast color. If not supplied, defaults to [ZetaContrast.aa]. + /// [primary] A color swatch for primary color accent. Defaults to null. + /// [secondary] A color swatch for secondary color accent. Defaults to null. + /// [error] A color swatch for error states. Defaults to null. + /// [cool] A color swatch for cooler color tones. Defaults to null. + /// [warm] A color swatch for warmer color tones. Defaults to null. + /// [white] A color option for white color. Defaults to null. + /// [black] A color option for black color. Defaults to null. + /// [link] A color option for links. Defaults to null. + /// [linkVisited] A color option for visited links. Defaults to null. + /// [shadow] A color option for shadows. Defaults to null. + factory ZetaColors.dark({ + ZetaContrast contrast = ZetaContrast.aa, + ZetaColorSwatch? primary, + ZetaColorSwatch? secondary, + ZetaColorSwatch? error, + ZetaColorSwatch? cool, + ZetaColorSwatch? warm, Color? white, - this.shadow = ZetaColorBase.shadow, - this.isDarkMode = false, - this.isAAA = false, - }) : _primary = primary?.zetaColorSwatch ?? ZetaColorBase.blue, - _secondary = secondary?.zetaColorSwatch ?? ZetaColorBase.blue, - _cool = cool?.zetaColorSwatch ?? ZetaColorBase.greyCool, - _warm = warm?.zetaColorSwatch ?? ZetaColorBase.greyWarm, - _blue = blue?.zetaColorSwatch ?? ZetaColorBase.blue, - _green = green?.zetaColorSwatch ?? ZetaColorBase.green, - _red = red?.zetaColorSwatch ?? ZetaColorBase.red, - _orange = orange?.zetaColorSwatch ?? ZetaColorBase.orange, - _purple = purple?.zetaColorSwatch ?? ZetaColorBase.purple, - _yellow = yellow?.zetaColorSwatch ?? ZetaColorBase.yellow, - _teal = teal?.zetaColorSwatch ?? ZetaColorBase.teal, - _pink = pink?.zetaColorSwatch ?? ZetaColorBase.pink, - _linkLight = linkLight ?? ZetaColorBase.linkLight, - _linkVisitedLight = linkVisitedLight ?? ZetaColorBase.linkVisitedLight, - _linkDark = linkDark ?? ZetaColorBase.linkDark, - _linkVisitedDark = linkVisitedDark ?? ZetaColorBase.linkVisitedDark, - _onPrimary = onPrimary, - _onSecondary = onSecondary, - _onNegative = onNegative, - _background = background, - _onBackground = onBackground, - _surface = surface, - _onSurface = onSurface, - _black = black, - _white = white, - _generatedColors = {}; - - /// Returns a new ZetaColors instance, with the copied fields applied. + Color? black, + Color? link, + Color? linkVisited, + Color? shadow, + }) { + return ZetaColors( + cool: cool, + warm: warm, + white: white ?? ZetaColorBase.white, + black: black ?? ZetaColorBase.black, + primary: primary, + contrast: contrast, + secondary: secondary, + error: error, + brightness: Brightness.dark, + surfaceTertiary: warm?.shade10, + surfaceSecondary: cool?.shade10, + surfacePrimary: black ?? ZetaColorBase.black, + link: link ?? ZetaColorBase.linkDark, + shadow: shadow ?? ZetaColorBase.shadowLight, + linkVisited: linkVisited ?? ZetaColorBase.linkVisitedDark, + ); + } + + /// Applies new property values to [ZetaColors] and returns a new copy. + /// + /// Each property defaults to the previous value if not specified. ZetaColors copyWith({ - bool? isDarkMode, - bool? isAAA, + Brightness? brightness, + ZetaContrast? contrast, ZetaColorSwatch? primary, ZetaColorSwatch? secondary, + ZetaColorSwatch? error, ZetaColorSwatch? cool, ZetaColorSwatch? warm, - ZetaColorSwatch? blue, - ZetaColorSwatch? green, - ZetaColorSwatch? red, - ZetaColorSwatch? orange, - ZetaColorSwatch? purple, - ZetaColorSwatch? yellow, - ZetaColorSwatch? teal, - ZetaColorSwatch? pink, - Color? linkLight, - Color? linkVisitedLight, - Color? linkDark, - Color? linkVisitedDark, + Color? white, + Color? black, + Color? shadow, + Color? link, + Color? linkVisited, + Color? surfacePrimary, + Color? surfaceSecondary, + Color? surfaceTertiary, }) { return ZetaColors( - primary: primary ?? _primary, + white: white ?? this.white, + black: black ?? this.black, + brightness: brightness ?? this.brightness, + contrast: contrast ?? this.contrast, + primary: primary ?? this.primary, secondary: secondary ?? this.secondary, - cool: cool ?? _cool, - warm: warm ?? _warm, - blue: blue ?? _blue, - green: green ?? _green, - red: red ?? _red, - orange: orange ?? _orange, - purple: purple ?? _purple, - yellow: yellow ?? _yellow, - teal: teal ?? _teal, - pink: pink ?? _pink, - linkLight: linkLight ?? _linkLight, - linkVisitedLight: linkVisitedLight ?? _linkVisitedLight, - linkDark: linkDark ?? _linkDark, - linkVisitedDark: linkVisitedDark ?? _linkVisitedDark, - isDarkMode: isDarkMode ?? this.isDarkMode, - isAAA: isAAA ?? this.isAAA, + error: error ?? this.error, + cool: cool ?? this.cool, + warm: warm ?? this.warm, + shadow: shadow ?? this.shadow, + link: link ?? this.link, + linkVisited: linkVisited ?? this.linkVisited, + surfacePrimary: surfacePrimary ?? this.surfacePrimary, + surfaceSecondary: surfaceSecondary ?? this.surfaceSecondary, + surfaceTertiary: surfaceTertiary ?? this.surfaceTertiary, + adjust: (brightness != null && brightness != this.brightness) || (contrast != null && contrast != this.contrast), ); } - /// Calculates foreground color based on luminance of input. - static Color computeForeground({ - required Color input, - Color light = ZetaColorBase.white, - Color dark = ZetaColorBase.black, - }) => - input.isLight ? dark : light; - - /// Calculates foreground color based on luminance of input. - Color computeForegroundFromTheme({required Color input}) => input.isLight ? textDefault : textInverse; - - /// Setter for updating ZetaColors instance based on current context. - static void setColors(BuildContext context, ZetaColors colors) { - final ZetaState? state = context.findAncestorStateOfType(); - if (state != null) { - state.colors = colors; - } + /// Apply the given contrast to the color scheme and return a new color scheme. + /// + /// If the contrast is the same as the current one, this method will return the current color scheme. + ZetaColors apply({ + required ZetaContrast contrast, + }) { + if (contrast == this.contrast) return this; + return copyWith( + contrast: contrast, + ); } - /// Setter for updating if dark mode is activated for ZetaColors instance based on current context. - static void setDarkMode(BuildContext context, bool val) { - final ZetaState? state = context.findAncestorStateOfType(); - if (state != null) { - state.colors = state.colors.copyWith(isDarkMode: val); - } + /// Returns a [ZetaColorScheme] based on the properties of the current [ZetaColors]. + ZetaColorScheme toScheme() { + final effectivePrimary = primary.shade(contrast.primary); + final effectiveSecondary = secondary.shade(contrast.primary); + final effectiveSurface = surfacePrimary; + final effectiveError = error; + + return ZetaColorScheme( + zetaColors: this, + brightness: brightness, + background: surfaceTertiary, + error: effectiveError, + onBackground: textDefault, + onError: effectiveError.onColor, + onPrimary: effectivePrimary.onColor, + onSecondary: effectiveSecondary.onColor, + onSurface: textDefault, + primary: effectivePrimary, + secondary: effectiveSecondary, + surface: effectiveSurface, + ); } - /// Returns instance of [ZetaColors] from the current context. - /// - /// For this function to work properly, it is required that the current context is beneath a [ZetaColors] widget - /// otherwise a new default instance of [ZetaColors] will be returned. - static ZetaColors of(BuildContext context) { - final ZetaState? state = context.findAncestorStateOfType(); + @override + bool operator ==(Object other) => + identical(this, other) || + other is ZetaColors && + runtimeType == other.runtimeType && + brightness == other.brightness && + contrast == other.contrast && + primary == other.primary && + secondary == other.secondary && + error == other.error && + cool == other.cool && + warm == other.warm && + shadow == other.shadow && + white == other.white && + black == other.black && + link == other.link && + linkVisited == other.linkVisited && + surfacePrimary == other.surfacePrimary && + surfaceSecondary == other.surfaceSecondary && + surfaceTertiary == other.surfaceTertiary && + blue == other.blue && + green == other.green && + red == other.red && + orange == other.orange && + purple == other.purple && + yellow == other.yellow && + teal == other.teal && + pink == other.pink; - // ignore: prefer_const_constructors - return state?.colors ?? ZetaColors(); - } + @override + int get hashCode => + brightness.hashCode ^ + contrast.hashCode ^ + primary.hashCode ^ + secondary.hashCode ^ + error.hashCode ^ + cool.hashCode ^ + warm.hashCode ^ + shadow.hashCode ^ + white.hashCode ^ + black.hashCode ^ + link.hashCode ^ + linkVisited.hashCode ^ + surfacePrimary.hashCode ^ + surfaceSecondary.hashCode ^ + surfaceTertiary.hashCode ^ + blue.hashCode ^ + green.hashCode ^ + red.hashCode ^ + orange.hashCode ^ + purple.hashCode ^ + yellow.hashCode ^ + teal.hashCode ^ + pink.hashCode; } -/// A swatch of colors with values from 10 (light) to 100 (dark). -class ZetaColorSwatch extends ColorSwatch { - /// Returns an element of the swatch table. - @override - Color? operator [](int index) => super[isDarkMode ? (-1 * index) + 110 : index]; +enum _ZetaColorProperties { + primarySwatch, + secondarySwatch, + cool, + warm, + shadow, + textDefault, + textSubtle, + pink, + teal, + yellow, + purple, + orange, + red, + green, + blue, + surfaceSelected, + surfaceSelectedHovered, + surfaceHovered, + surfaceDisabled, + surfaceTertiary, + surfaceSecondary, + surfacePrimary, + linkVisited, + link, + borderSelected, + borderDisabled, + borderSubtle, + borderDefault, + textInverse, + textDisabled, +} + +/// Custom extension on ColorScheme which makes [ZetaColors] available through theme context. +/// +/// A customizable, token-based color palette, adapting Zeta colors to Flutter's colorScheme. +extension ZetaColorGetters on ColorScheme { + ZetaColorScheme? get _resolve => this is ZetaColorScheme ? this as ZetaColorScheme : null; + + /// Represents the Zeta accessibility standard. + ZetaContrast get contrast => _resolve?.zetaColors.contrast ?? ZetaContrast.aa; - /// True if swatch should be generated for dark mode. + /// Primary color swatch. + /// + /// Defaults to [ZetaColorBase.blue]. /// /// {@macro zeta-color-dark} - final bool isDarkMode; + ZetaColorSwatch get primarySwatch => + _resolve?.zetaColors.primary ?? _resolveDefault(_ZetaColorProperties.primarySwatch); - /// True if swatch should be generated for AAA accessability mode. + /// Secondary color used in app. /// - /// {@macro zeta-color-aaa} - final bool isAAA; - - /// The lightest shade. - Color get shade10 => this[10]!; - - /// The second lightest shade. - Color get shade20 => this[20]!; - - /// The third lightest shade. - Color get shade30 => this[30]!; + /// Defaults to `ZetaColors.cool.90`. + /// + /// Maps to [ColorScheme.secondary]. + ZetaColorSwatch get secondarySwatch => + _resolve?.zetaColors.secondary ?? _resolveDefault(_ZetaColorProperties.secondarySwatch); - /// The fourth lightest shade. - Color get shade40 => this[40]!; + /// Cool grey color swatch. + /// + /// Defaults to [ZetaColorBase.greyCool]. + /// + /// {@macro zeta-color-dark} + ZetaColorSwatch get cool => _resolve?.zetaColors.cool ?? _resolveDefault(_ZetaColorProperties.cool); - /// The fifth lightest shade. - Color get shade50 => this[50]!; + /// Warm grey color swatch. + /// + /// Defaults to [ZetaColorBase.greyWarm]. + /// + /// {@macro zeta-color-dark} + ZetaColorSwatch get warm => _resolve?.zetaColors.warm ?? _resolveDefault(_ZetaColorProperties.warm); - /// The default shade. - Color get shade60 => this[60]!; + /// Shadow color. + /// + /// Maps to [ColorScheme.shadow]. + /// + /// Defaults to #49505E at 10% opacity. + Color get shadow => _resolve?.zetaColors.shadow ?? _resolveDefault(_ZetaColorProperties.shadow); - /// The fourth darkest shade. - Color get shade70 => this[70]!; + /// Cool grey color swatch. + /// + /// Defaults to [ZetaColorBase.greyCool]. + /// + /// {@macro zeta-color-dark} + Color get textDefault => _resolve?.zetaColors.textDefault ?? _resolveDefault(_ZetaColorProperties.textDefault); - /// The third darkest shade. - Color get shade80 => this[80]!; + /// Subtle text /icon color. + /// + /// Defaults to `ZetaColors.cool.70`. + /// + /// Maps to [ColorScheme.onBackground]. + /// + /// {@macro zeta-color-dark} + Color get textSubtle => _resolve?.zetaColors.textSubtle ?? _resolveDefault(_ZetaColorProperties.textSubtle); - /// The second darkest shade. - Color get shade90 => this[90]!; + /// Disabled text / icon color. + /// + /// Defaults to `ZetaColors.cool.50`. + /// + /// {@macro zeta-color-dark} + Color get textDisabled => _resolve?.zetaColors.textDisabled ?? _resolveDefault(_ZetaColorProperties.textDisabled); - /// The darkest shade. - Color get shade100 => this[100]!; + /// Inverse text / icon color. + /// + /// Used for text that is not on [ColorScheme.background] or [ThemeData.scaffoldBackgroundColor]. + /// + /// Defaults to `ZetaColors.cool.20`. + /// + /// {@macro zeta-color-dark} + Color get textInverse => _resolve?.zetaColors.textInverse ?? _resolveDefault(_ZetaColorProperties.textInverse); - /// Selected color: 80. - Color get selected => isAAA ? shade100 : shade80; + // Border variants. - /// Hover color: 70. - Color get hover => shade70; + /// Default border color. + /// + /// Defaults to `ZetaColors.warm.50`. + /// + /// {@macro zeta-color-dark} + Color get borderDefault => _resolve?.zetaColors.borderDefault ?? _resolveDefault(_ZetaColorProperties.borderDefault); - /// Text color: 60, or [ZetaColors.textDarkMode] if [isAAA]. + /// Subtle border color. /// - /// This color should be used for foreground text when [surface] is the background. - Color get text { - if (isAAA) return isDarkMode ? ZetaColorBase.white : ZetaColorBase.text; + /// `ZetaColors.cool.40`. + /// + /// {@macro zeta-color-dark} + Color get borderSubtle => _resolve?.zetaColors.borderSubtle ?? _resolveDefault(_ZetaColorProperties.borderSubtle); - return shade60; - } + /// Disabled border color. + /// + /// Defaults to `ZetaColors.cool.30`. + /// + /// {@macro zeta-color-dark} + Color get borderDisabled => + _resolve?.zetaColors.borderDisabled ?? _resolveDefault(_ZetaColorProperties.borderDisabled); - /// Default color for the swatch. + /// Selected border color. /// - /// Defaults to [shade60]. + /// Defaults to `ZetaColors.cool.90`. /// - /// {@macro zeta-color-aaa} - Color get primary => isAAA ? shade80 : shade60; + /// {@macro zeta-color-dark} + Color get borderSelected => + _resolve?.zetaColors.borderSelected ?? _resolveDefault(_ZetaColorProperties.borderSelected); + + // Links - /// Icon color: 60. + /// Link color. /// - /// This color should be used for foreground icons or graphics when [surface] is the background. - Color get icon => shade60; + /// Defaults to [ZetaColorBase.linkLight] or [ZetaColorBase.linkDark]. + Color get link => _resolve?.zetaColors.link ?? _resolveDefault(_ZetaColorProperties.link); - /// Border color: 60. - Color get border => shade60; + /// Link color. + /// + /// Defaults to [ZetaColorBase.linkVisitedLight] or [ZetaColorBase.linkVisitedDark]. + Color get linkVisited => _resolve?.zetaColors.linkVisited ?? _resolveDefault(_ZetaColorProperties.linkVisited); - /// Subtle border color: 40. - Color get borderSubtle => shade40; + // Backdrop colors. - /// Surface color: 10. + /// Surface color. /// - /// Used for component backgrounds. Use [text] and [icon] for foreground components. - Color get surface => shade10; + /// Maps to [ColorScheme.surface]. + /// + /// * Light mode: `ZetaColors.black` + /// * Dark mode: `ZetaColors.white`. + Color get surfacePrimary => + _resolve?.zetaColors.surfacePrimary ?? _resolveDefault(_ZetaColorProperties.surfacePrimary); - /// Returns the subtle color for the swatch. - Color get subtle => shade10; + /// Secondary surface color. + /// + /// + /// * `ZetaColors.cool.10`. + Color get surfaceSecondary => + _resolve?.zetaColors.surfaceSecondary ?? _resolveDefault(_ZetaColorProperties.surfaceSecondary); - /// Generates foreground color to be used on [primary]. - Color get on => ZetaColors.computeForeground(input: primary); + /// Tertiary surface color. + /// + /// Maps to [ColorScheme.background] and [ThemeData.scaffoldBackgroundColor] + /// + /// * `ZetaColors.warm.10`. + Color get surfaceTertiary => + _resolve?.zetaColors.surfaceTertiary ?? _resolveDefault(_ZetaColorProperties.surfaceTertiary); - /// Gets color for disabled mode. - Color get disabled => isDarkMode ? shade30 : shade10; + /// Disabled surface color. + /// + /// Defaults to `ZetaColors.cool.30`. + /// + /// {@macro zeta-color-dark} + Color get surfaceDisabled => + _resolve?.zetaColors.surfaceDisabled ?? _resolveDefault(_ZetaColorProperties.surfaceDisabled); - /// Constructs a [ZetaColorSwatch]. + /// Hover surface color. + /// + /// Defaults to `ZetaColors.cool.20`. /// - /// See also: - /// * [MaterialColor]. - const ZetaColorSwatch(super.primary, super._swatch, {this.isDarkMode = false, this.isAAA = false}); + /// {@macro zeta-color-dark} + Color get surfaceHovered => + _resolve?.zetaColors.surfaceHovered ?? _resolveDefault(_ZetaColorProperties.surfaceHovered); - /// {@template zeta.color.color_to_swatch} - /// Returns a color swatch from a single color. + /// Selected hover surface color. /// - /// This function does NOT guarantee color swatches will meet accessibility criteria AA or AAA. - /// {@endtemplate} - ZetaColorSwatch.fromColor(Color color, {this.isAAA = false, this.isDarkMode = false}) - : super( - color.value, - color._mapFromColor, - ); + /// Defaults to: `ZetaColors.blue.20`. + Color get surfaceSelectedHovered => + _resolve?.zetaColors.surfaceSelectedHovered ?? _resolveDefault(_ZetaColorProperties.surfaceSelectedHovered); - /// Creates a new instance of [ZetaColors] with the fields applied. - ZetaColorSwatch copyWith({bool isDarkMode = false, bool isAAA = false}) { - return ZetaColorSwatch( - isAAA ? shade80.value : shade60.value, - { - for (final v in List.generate(ZetaColors._minShade, (index) => (index + 1) * ZetaColors._minShade)) v: this[v]!, - }, - isDarkMode: isDarkMode, - isAAA: isAAA, - ); - } -} + /// Selected surface color. + /// + /// Defaults to: `ZetaColors.blue.10`. + Color get surfaceSelected => + _resolve?.zetaColors.surfaceSelected ?? _resolveDefault(_ZetaColorProperties.surfaceSelected); -/// Extensions on [Color]. -extension ColorExtension on Color { - /// {@macro zeta.color.color_to_swatch} - ZetaColorSwatch get zetaColorSwatch => ZetaColorSwatch.fromColor(this); + /// Blue color swatch. + /// + /// Defaults to [ZetaColorBase.blue]. + /// + /// {@macro zeta-color-dark} + ZetaColorSwatch get blue => _resolve?.zetaColors.blue ?? _resolveDefault(_ZetaColorProperties.blue); - /// Applies lightness percentage to color. - Color withLightness(double percentage) { - final HSLColor hslColor = HSLColor.fromColor(this); + /// Green color swatch. + /// + /// Defaults to [ZetaColorBase.green]. + /// + /// {@macro zeta-color-dark} + ZetaColorSwatch get green => _resolve?.zetaColors.green ?? _resolveDefault(_ZetaColorProperties.green); - return hslColor.withLightness(percentage).toColor(); - } + /// Red color swatch. + /// + /// Defaults to [ZetaColorBase.red]. + /// + /// {@macro zeta-color-dark} + ZetaColorSwatch get red => _resolve?.zetaColors.red ?? _resolveDefault(_ZetaColorProperties.red); - /// Uses [computeLuminance] to determine if a color if light. - bool get isLight => computeLuminance() > 0.5; + /// Orange color swatch. + /// + /// Defaults to [ZetaColorBase.orange]. + /// + /// {@macro zeta-color-dark} + ZetaColorSwatch get orange => _resolve?.zetaColors.orange ?? _resolveDefault(_ZetaColorProperties.orange); - /// Gets lightness of color. - double get lightness => HSLColor.fromColor(this).lightness; + /// Purple color swatch. + /// + /// Defaults to [ZetaColorBase.purple]. + /// + /// {@macro zeta-color-dark} + ZetaColorSwatch get purple => _resolve?.zetaColors.purple ?? _resolveDefault(_ZetaColorProperties.purple); - /// Calculates the contrast of a color. - double contrast({Color background = Colors.white}) { - final l1 = computeLuminance(); - final l2 = background.computeLuminance(); - const offset = 0.05; + /// Yellow color swatch. + /// + /// Defaults to [ZetaColorBase.yellow]. + /// + /// {@macro zeta-color-dark} + ZetaColorSwatch get yellow => _resolve?.zetaColors.yellow ?? _resolveDefault(_ZetaColorProperties.yellow); - return (max(l1, l2) + offset) / (min(l1, l2) + offset); - } + /// Teal color swatch. + /// + /// Defaults to [ZetaColorBase.teal]. + /// + /// {@macro zeta-color-dark} + ZetaColorSwatch get teal => _resolve?.zetaColors.teal ?? _resolveDefault(_ZetaColorProperties.teal); - /// Rudimentary algorithm to generate a color swatch from a single color. + /// Pink color swatch. /// - /// Attempts to ensure 60 will pass AA against white and 80 will pass AAA against white. - /// This is NOT guaranteed to work. + /// Defaults to [ZetaColorBase.pink]. /// - // TODO(colors): Improve this. - Map get _mapFromColor { - final initialContrast = contrast(); + /// {@macro zeta-color-dark} + ZetaColorSwatch get pink => _resolve?.zetaColors.pink ?? _resolveDefault(_ZetaColorProperties.pink); - double findX(Color c, double minContrast) { - double min = 0; - double max = 1; - bool found = false; - double val; + // Alert Colors - do { - val = (min + max) / 2; + /// Green positive color. + /// + /// Defaults to `ZetaColors.green.60` in AA system. + /// Defaults to `ZetaColors.green.80` in AAA system. + /// + /// {@macro zeta-color-dark} + /// + /// {@macro zeta-color-aaa} + Color get positive => green; - final con = c.withLightness(val).contrast(); + /// Red negative color. + /// + /// Defaults to `ZetaColors.red.60` in AA system. + /// Defaults to `ZetaColors.red.80` in AAA system. + /// + /// Maps to [ColorScheme.error]. + /// + /// {@macro zeta-color-dark} + /// + /// {@macro zeta-color-aaa} + Color get negative => red; - if (con < minContrast) { - max = val; - } else if (con > minContrast + 0.1) { - min = val; - } else { - found = true; - } - } while (!found); + /// Orange warning color. + /// + /// Defaults to `ZetaColors.orange.60` in AA system. + /// Defaults to `ZetaColors.orange.80` in AAA system. + /// + /// {@macro zeta-color-dark} + /// + /// {@macro zeta-color-aaa} + Color get warning => orange; - return val; + /// Purple info color. + /// + /// Defaults to `ZetaColors.purple.60` in AA system. + /// Defaults to `ZetaColors.purple.80` in AAA system. + /// + /// {@macro zeta-color-dark} + /// + /// {@macro zeta-color-aaa} + Color get info => purple; + + T _resolveDefault(_ZetaColorProperties property) { + switch (property) { + case _ZetaColorProperties.primarySwatch: + case _ZetaColorProperties.secondarySwatch: + return ZetaColorBase.blue.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.cool: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.warm: + return ZetaColorBase.greyWarm.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.textDefault: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade90 as T; + case _ZetaColorProperties.textSubtle: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade70 as T; + case _ZetaColorProperties.textInverse: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade20 as T; + case _ZetaColorProperties.textDisabled: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade50 as T; + case _ZetaColorProperties.pink: + return ZetaColorBase.pink.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.teal: + return ZetaColorBase.teal.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.yellow: + return ZetaColorBase.yellow.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.purple: + return ZetaColorBase.purple.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.orange: + return ZetaColorBase.orange.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.red: + return ZetaColorBase.red.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.green: + return ZetaColorBase.green.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.blue: + return ZetaColorBase.blue.apply(brightness: brightness, contrast: contrast) as T; + case _ZetaColorProperties.surfaceSelected: + return ZetaColorBase.blue.apply(brightness: brightness, contrast: contrast).shade10 as T; + case _ZetaColorProperties.surfaceSelectedHovered: + return ZetaColorBase.blue.apply(brightness: brightness, contrast: contrast).shade20 as T; + case _ZetaColorProperties.surfaceHovered: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade20 as T; + case _ZetaColorProperties.surfaceDisabled: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade30 as T; + case _ZetaColorProperties.surfaceTertiary: + return ZetaColorBase.greyWarm.apply(brightness: brightness, contrast: contrast).shade10 as T; + case _ZetaColorProperties.surfaceSecondary: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade10 as T; + case _ZetaColorProperties.borderSelected: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade90 as T; + case _ZetaColorProperties.borderDisabled: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade30 as T; + case _ZetaColorProperties.borderSubtle: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade40 as T; + case _ZetaColorProperties.borderDefault: + return ZetaColorBase.greyCool.apply(brightness: brightness, contrast: contrast).shade50 as T; + case _ZetaColorProperties.shadow: + return (brightness == Brightness.light ? ZetaColorBase.shadowLight : ZetaColorBase.shadowDark) as T; + case _ZetaColorProperties.surfacePrimary: + return (brightness == Brightness.light ? ZetaColorBase.white : ZetaColorBase.black) as T; + case _ZetaColorProperties.linkVisited: + return (brightness == Brightness.light ? ZetaColorBase.linkVisitedLight : ZetaColorBase.linkVisitedDark) as T; + case _ZetaColorProperties.link: + return (brightness == Brightness.light ? ZetaColorBase.linkLight : ZetaColorBase.linkVisitedDark) as T; } - - final double col60 = initialContrast < 4.6 && initialContrast > 4.5 ? lightness : findX(this, 4.5); - final double col80 = findX(this, 7); - - final double darkGap = col80 / 3; - final double lightGap = (1 - col60) / 6; - - return { - 100: withLightness(col80 - (darkGap * 2)), - 90: withLightness(col80 - (darkGap * 1)), - 80: withLightness(col80), - 70: withLightness((col80 + col60) / 2), - 60: withLightness(col60), - 50: withLightness(col60 + (lightGap * 1)), - 40: withLightness(col60 + (lightGap * 2)), - 30: withLightness(col60 + (lightGap * 3)), - 20: withLightness(col60 + (lightGap * 4)), - 10: withLightness(col60 + (lightGap * 5)), - }; } } diff --git a/lib/src/theme/colors_base.dart b/lib/src/theme/colors_base.dart index 36d2a5e6..dbd25a42 100644 --- a/lib/src/theme/colors_base.dart +++ b/lib/src/theme/colors_base.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; -import '../../../zeta_flutter.dart'; +import 'color_swatch.dart'; +import 'colors.dart'; /// Default set of Zeta Colors that can be used to make a [ZetaColors] instance. /// @@ -17,6 +18,8 @@ import '../../../zeta_flutter.dart'; /// * [greyWarm] /// * [greyCool]. class ZetaColorBase { + ZetaColorBase._(); + /// Blue swatch. /// /// {@template zeta-colors-swatch} @@ -25,162 +28,192 @@ class ZetaColorBase { /// See also: /// * [ZetaColorSwatch]. /// {@endtemplate} - static const ZetaColorSwatch blue = ZetaColorSwatch(0xFF0073e6, { - 100: Color(0xFF101B25), - 90: Color(0xFF002C58), - 80: Color(0xFF004D99), - 70: Color(0xFF0061C2), - 60: Color(0xFF0073E6), - 50: Color(0xFF599FE5), - 40: Color(0xFF7EBEFF), - 30: Color(0xFFB7DBFF), - 20: Color(0xFFE2F1FF), - 10: Color(0xFFF1F8FF), - }); + static const ZetaColorSwatch blue = ZetaColorSwatch( + primary: 0xFF0073e6, + swatch: { + 100: Color(0xFF101B25), + 90: Color(0xFF002C58), + 80: Color(0xFF004D99), + 70: Color(0xFF0061C2), + 60: Color(0xFF0073E6), + 50: Color(0xFF599FE5), + 40: Color(0xFF7EBEFF), + 30: Color(0xFFB7DBFF), + 20: Color(0xFFE2F1FF), + 10: Color(0xFFF1F8FF), + }, + ); /// Green swatch. /// /// {@macro zeta-colors-swatch} - static const ZetaColorSwatch green = ZetaColorSwatch(0xFF00864F, { - 100: Color(0xFF081711), - 90: Color(0xFF00331E), - 80: Color(0xFF005F38), - 70: Color(0xFF006D3F), - 60: Color(0xFF00864F), - 50: Color(0xFF67B796), - 40: Color(0xFF84DAB6), - 30: Color(0xFFBEEFDB), - 20: Color(0xFFD8FFEF), - 10: Color(0xFFECFFF7), - }); + static const ZetaColorSwatch green = ZetaColorSwatch( + primary: 0xFF00864F, + swatch: { + 100: Color(0xFF081711), + 90: Color(0xFF00331E), + 80: Color(0xFF005F38), + 70: Color(0xFF006D3F), + 60: Color(0xFF00864F), + 50: Color(0xFF67B796), + 40: Color(0xFF84DAB6), + 30: Color(0xFFBEEFDB), + 20: Color(0xFFD8FFEF), + 10: Color(0xFFECFFF7), + }, + ); /// Red swatch. /// /// {@macro zeta-colors-swatch} - static const ZetaColorSwatch red = ZetaColorSwatch(0xFFD70015, { - 100: Color(0xFF220F11), - 90: Color(0xFF520008), - 80: Color(0xFF8F000E), - 70: Color(0xFFB50012), - 60: Color(0xFFD70015), - 50: Color(0xFFF36170), - 40: Color(0xFFF98C97), - 30: Color(0xFFFFB3BB), - 20: Color(0xFFFFE1E4), - 10: Color(0xFFFFF0F1), - }); + static const ZetaColorSwatch red = ZetaColorSwatch( + primary: 0xFFD70015, + swatch: { + 100: Color(0xFF220F11), + 90: Color(0xFF520008), + 80: Color(0xFF8F000E), + 70: Color(0xFFB50012), + 60: Color(0xFFD70015), + 50: Color(0xFFF36170), + 40: Color(0xFFF98C97), + 30: Color(0xFFFFB3BB), + 20: Color(0xFFFFE1E4), + 10: Color(0xFFFFF0F1), + }, + ); /// Orange swatch. /// /// {@macro zeta-colors-swatch} - static const ZetaColorSwatch orange = ZetaColorSwatch(0xFFC87500, { - 100: Color(0xFF1E1100), - 90: Color(0xFF402600), - 80: Color(0xFF764502), - 70: Color(0xFF965802), - 60: Color(0xFFAE6500), - 50: Color(0xFFD78E26), - 40: Color(0xFFF5A230), - 30: Color(0xFFFFB348), - 20: Color(0xFFFFD292), - 10: Color(0xFFFFE7C6), - }); + static const ZetaColorSwatch orange = ZetaColorSwatch( + primary: 0xFFC87500, + swatch: { + 100: Color(0xFF1E1100), + 90: Color(0xFF402600), + 80: Color(0xFF764502), + 70: Color(0xFF965802), + 60: Color(0xFFAE6500), + 50: Color(0xFFD78E26), + 40: Color(0xFFF5A230), + 30: Color(0xFFFFB348), + 20: Color(0xFFFFD292), + 10: Color(0xFFFFE7C6), + }, + ); /// Purple swatch. /// /// {@macro zeta-colors-swatch} - static const ZetaColorSwatch purple = ZetaColorSwatch(0xFF6400D6, { - 100: Color(0xFF180F22), - 90: Color(0xFF260052), - 80: Color(0xFF43008F), - 70: Color(0xFF6400D6), - 60: Color(0xFF7E0CFF), - 50: Color(0xFF9B71DF), - 40: Color(0xFFCEA4FF), - 30: Color(0xFFDCC1FB), - 20: Color(0xFFEFE1FF), - 10: Color(0xFFF7F0FF), - }); + static const ZetaColorSwatch purple = ZetaColorSwatch( + primary: 0xFF6400D6, + swatch: { + 100: Color(0xFF180F22), + 90: Color(0xFF260052), + 80: Color(0xFF43008F), + 70: Color(0xFF6400D6), + 60: Color(0xFF7E0CFF), + 50: Color(0xFF9B71DF), + 40: Color(0xFFCEA4FF), + 30: Color(0xFFDCC1FB), + 20: Color(0xFFEFE1FF), + 10: Color(0xFFF7F0FF), + }, + ); /// Yellow swatch. /// /// {@macro zeta-colors-swatch} - static const ZetaColorSwatch yellow = ZetaColorSwatch(0xFFA38600, { - 100: Color(0xFF181400), - 90: Color(0xFF352B00), - 80: Color(0xFF564908), - 70: Color(0xFF766200), - 60: Color(0xFF8D7400), - 50: Color(0xFFC2A728), - 40: Color(0xFFDBB91C), - 30: Color(0xFFF3D961), - 20: Color(0xFFFFEA89), - 10: Color(0xFFFFF7D4), - }); + static const ZetaColorSwatch yellow = ZetaColorSwatch( + primary: 0xFFA38600, + swatch: { + 100: Color(0xFF181400), + 90: Color(0xFF352B00), + 80: Color(0xFF564908), + 70: Color(0xFF766200), + 60: Color(0xFF8D7400), + 50: Color(0xFFC2A728), + 40: Color(0xFFDBB91C), + 30: Color(0xFFF3D961), + 20: Color(0xFFFFEA89), + 10: Color(0xFFFFF7D4), + }, + ); /// Teal swatch. /// /// {@macro zeta-colors-swatch} - static const ZetaColorSwatch teal = ZetaColorSwatch(0xFF018786, { - 100: Color(0xFF0A1616), - 90: Color(0xFF003535), - 80: Color(0xFF005B5B), - 70: Color(0xFF017474), - 60: Color(0xFF1A8080), - 50: Color(0xFF65C5C5), - 40: Color(0xFF91E1E1), - 30: Color(0xFFBCFBFB), - 20: Color(0xFFD9FFFF), - 10: Color(0xFFECFFFF), - }); + static const ZetaColorSwatch teal = ZetaColorSwatch( + primary: 0xFF018786, + swatch: { + 100: Color(0xFF0A1616), + 90: Color(0xFF003535), + 80: Color(0xFF005B5B), + 70: Color(0xFF017474), + 60: Color(0xFF1A8080), + 50: Color(0xFF65C5C5), + 40: Color(0xFF91E1E1), + 30: Color(0xFFBCFBFB), + 20: Color(0xFFD9FFFF), + 10: Color(0xFFECFFFF), + }, + ); /// Pink swatch. /// /// {@macro zeta-colors-swatch} - static const ZetaColorSwatch pink = ZetaColorSwatch(0xFFAB006D, { - 100: Color(0xFF2E001E), - 90: Color(0xFF640040), - 80: Color(0xFF840054), - 70: Color(0xFFAB006D), - 60: Color(0xFFD30589), - 50: Color(0xFFEE78C3), - 40: Color(0xFFFF94D8), - 30: Color(0xFFFFBEE7), - 20: Color(0xFFFFE3F5), - 10: Color(0xFFFFF7FC), - }); + static const ZetaColorSwatch pink = ZetaColorSwatch( + primary: 0xFFAB006D, + swatch: { + 100: Color(0xFF2E001E), + 90: Color(0xFF640040), + 80: Color(0xFF840054), + 70: Color(0xFFAB006D), + 60: Color(0xFFD30589), + 50: Color(0xFFEE78C3), + 40: Color(0xFFFF94D8), + 30: Color(0xFFFFBEE7), + 20: Color(0xFFFFE3F5), + 10: Color(0xFFFFF7FC), + }, + ); /// Grey warm swatch. /// /// {@macro zeta-colors-swatch} - static const ZetaColorSwatch greyWarm = ZetaColorSwatch(0xFF858585, { - 100: Color(0xFF151519), - 90: Color(0xFF202020), - 80: Color(0xFF313131), - 70: Color(0xFF585858), - 60: Color(0xFF858585), - 50: Color(0xFFB9B9B9), - 40: Color(0xFFDEDEDE), - 30: Color(0xFFEDEDED), - 20: Color(0xFFF7F7F7), - 10: Color(0xFFFAFAFA), - }); + static const ZetaColorSwatch greyWarm = ZetaColorSwatch( + primary: 0xFF858585, + swatch: { + 100: Color(0xFF151519), + 90: Color(0xFF202020), + 80: Color(0xFF313131), + 70: Color(0xFF585858), + 60: Color(0xFF858585), + 50: Color(0xFFB9B9B9), + 40: Color(0xFFDEDEDE), + 30: Color(0xFFEDEDED), + 20: Color(0xFFF7F7F7), + 10: Color(0xFFFAFAFA), + }, + ); /// Grey cool swatch. /// /// {@macro zeta-colors-swatch} - static const ZetaColorSwatch greyCool = ZetaColorSwatch(0xFF7A8190, { - 100: Color(0xFF0C0D0E), - 90: Color(0xFF1D1E23), - 80: Color(0xFF2C2F36), - 70: Color(0xFF545963), - 60: Color(0xFF7A8190), - 50: Color(0xFF8D95A3), - 40: Color(0xFFCED2DB), - 30: Color(0xFFE0E3E9), - 20: Color(0xFFF3F6FA), - 10: Color(0xFFF8FBFF), - }); + static const ZetaColorSwatch greyCool = ZetaColorSwatch( + primary: 0xFF7A8190, + swatch: { + 100: Color(0xFF0C0D0E), + 90: Color(0xFF1D1E23), + 80: Color(0xFF2C2F36), + 70: Color(0xFF545963), + 60: Color(0xFF7A8190), + 50: Color(0xFF8D95A3), + 40: Color(0xFFCED2DB), + 30: Color(0xFFE0E3E9), + 20: Color(0xFFF3F6FA), + 10: Color(0xFFF8FBFF), + }, + ); /// Pure white. /// @@ -208,5 +241,8 @@ class ZetaColorBase { static const Color linkVisitedDark = Color(0xFF47A3FF); /// Default shadow color. - static const Color shadow = Color(0x1A49505E); + static const Color shadowLight = Color(0x1A49505E); + + /// Default shadow color. + static const Color shadowDark = Color(0x1A49505E); } diff --git a/lib/src/theme/constants.dart b/lib/src/theme/constants.dart index 8b137891..92d3d64b 100644 --- a/lib/src/theme/constants.dart +++ b/lib/src/theme/constants.dart @@ -1 +1,21 @@ +import 'color_swatch.dart'; +/// Default font family for Zeta System +const kZetaFontFamily = 'packages/zeta_flutter/IBMPlexSans'; + +/// Primary color shade index for [ZetaColorSwatch] +const kZetaSwatchPrimaryIndex = 60; + +/// Target contrast values for different color shades of [ZetaColorSwatch] +const kZetaSwatchTargetContrasts = { + 100: 17.42, + 90: 13.99, + 80: 8.33, + 70: 6.02, + 60: 4.57, + 50: 2.66, + 40: 1.83, + 30: 1.23, + 20: 1.09, + 10: 1.02, +}; diff --git a/lib/src/theme/contrast.dart b/lib/src/theme/contrast.dart new file mode 100644 index 00000000..d406dc38 --- /dev/null +++ b/lib/src/theme/contrast.dart @@ -0,0 +1,149 @@ +/// ZetaAccessibilityStandard is an enumeration that defines the Web Content Accessibility Guidelines (WCAG) 2.1. +/// It includes two levels of conformance: AA (minimum) and AAA (enhanced). +enum ZetaContrast { + /// AA: The contrast ratio should be at least 4.57:1 + aa, + + /// AAA: The contrast ratio should be at least 8.33:1 + aaa, +} + +/// Extension on [ZetaContrast] to provide color indices +/// for certain accessibility scenarios +extension AccessibilityIndices on ZetaContrast { + /// Returns the color index value for a primary depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 60. + /// For [ZetaContrast.aaa], it returns 80. + int get primary { + switch (this) { + case ZetaContrast.aa: + return 60; + case ZetaContrast.aaa: + return 80; + } + } + + /// Returns the color index value for a surface depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 60. + /// For [ZetaContrast.aaa], it returns 80. + int get text { + switch (this) { + case ZetaContrast.aa: + return 60; + case ZetaContrast.aaa: + return 80; + } + } + + /// Returns the color index value for an icon depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 60. + /// For [ZetaContrast.aaa], it returns 80. + int get icon { + switch (this) { + case ZetaContrast.aa: + return 60; + case ZetaContrast.aaa: + return 80; + } + } + + /// Returns the color index value for a hover state depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 70. + /// For [ZetaContrast.aaa], it returns 90. + int get hover { + switch (this) { + case ZetaContrast.aa: + return 70; + case ZetaContrast.aaa: + return 90; + } + } + + /// Returns the color index value for a selected state depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 80. + /// For [ZetaContrast.aaa], it returns 100. + int get selected { + switch (this) { + case ZetaContrast.aa: + return 80; + case ZetaContrast.aaa: + return 100; + } + } + + /// Returns the color index value for a focus state depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 80. + /// For [ZetaContrast.aaa], it returns 100. + int get focus { + switch (this) { + case ZetaContrast.aa: + return 80; + case ZetaContrast.aaa: + return 100; + } + } + + /// Returns the color index value for a border depending on the ZetaContrast value. + /// + /// For [ZetaContrast.aa], it returns 60. + /// For [ZetaContrast.aaa], it returns 80. + int get border { + switch (this) { + case ZetaContrast.aa: + return 60; + case ZetaContrast.aaa: + return 80; + } + } + + /// Returns the color index value for a subtle visual element depending on the ZetaContrast value. + /// + /// For both [ZetaContrast.aa] and [ZetaContrast.aaa], it returns 40. + int get subtle { + switch (this) { + case ZetaContrast.aa: + return 40; + case ZetaContrast.aaa: + return 60; + } + } + + /// Returns the color index value for a surface depending on the ZetaContrast value. + /// + /// For both [ZetaContrast.aa] and [ZetaContrast.aaa], it returns 10. + int get surface { + switch (this) { + case ZetaContrast.aa: + return 10; + case ZetaContrast.aaa: + return 10; + } + } + + /// Returns the target contrast value. + /// + /// The getter, `targetContrast`, returns a double value that represents the + /// contrast ratio of a ZetaContrast object. The ratio can be either `4.53` or `8.33`, + /// depending on whether the contrast level of this instance is `ZetaContrast.aa` or `ZetaContrast.aaa`. + /// + /// * When the object's contrast level is `aa`, the method returns `4.53`. + /// * When the object's contrast level is `aaa`, the method returns `8.33`. + /// + /// These values serve as benchmarks for the contrast between the colours on a app's text + /// and background. Being able to measure and adjust this contrast plays a critical role in + /// improving a app's accessibility. + double get targetContrast { + switch (this) { + case ZetaContrast.aa: + return 4.53; + case ZetaContrast.aaa: + return 8.33; + } + } +} diff --git a/lib/src/theme/theme.dart b/lib/src/theme/theme.dart deleted file mode 100644 index aed90f93..00000000 --- a/lib/src/theme/theme.dart +++ /dev/null @@ -1,128 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../../zeta_flutter.dart'; - -export 'breakpoints.dart'; -export 'colors.dart'; -export 'colors_base.dart'; -export 'constants.dart'; - -/// Theme for Zeta. -class ZetaTheme { - /// Default theme in light mode. - /// - /// {@template zeta-theme} - /// For apps using Zeta components to be properly themed, [Zeta] ThemeData should be placed in the widget tree using one of the following methods: - /// - /// - /// dart``` - /// MaterialApp( - /// theme: zeta, - /// ... - /// ) - /// ``` - /// - /// ``` - /// Theme( - /// data: zeta, - /// child: ... - /// ) - /// ``` - /// - /// This adds all the appropriate tokens into the app to render correctly. - /// - /// {@endtemplate} - static ThemeData zetaLight({ZetaThemeData? initialTheme}) { - return ThemeData( - colorScheme: initialTheme?.colorScheme ?? initialTheme?.zetaColors?.toColorScheme ?? ZetaColors().toColorScheme, - fontFamily: initialTheme?.fontFamily ?? 'packages/zeta_flutter/IBMPlexSans', - // TODO(tokens): reinstate tokens.Typography.fontFamily when we have a plan for tokens - textTheme: initialTheme?.textTheme ?? ZetaText.textTheme, - ); - } - - /// Default theme with dark mode. - /// - /// {@macro zeta-theme} - static ThemeData zetaDark({ZetaThemeData? initialTheme}) { - return ThemeData( - colorScheme: initialTheme?.colorScheme ?? - initialTheme?.zetaColors?.copyWith(isDarkMode: true).toColorScheme ?? - ZetaColors().toColorScheme, - fontFamily: initialTheme?.fontFamily ?? 'packages/zeta_flutter/IBMPlexSans', - textTheme: initialTheme?.textTheme ?? ZetaText.textTheme, - ); - } - - /// Builds a [ThemeData] based off of some initial data. - /// - /// If `initialTheme.zetaColors` is not defined, colors will default to light mode. - /// - /// {@macro zeta-theme} - static ThemeData builder({ZetaThemeData? initialTheme}) { - final ZetaColors colors = initialTheme?.zetaColors ?? ZetaColors(); - - return ThemeData( - colorScheme: initialTheme?.colorScheme ?? colors.toColorScheme, - fontFamily: initialTheme?.fontFamily ?? 'packages/zeta_flutter/IBMPlexSans', - textTheme: initialTheme?.textTheme ?? ZetaText.textThemeBuilder(colors), - ); - } -} - -/// Basic theme information used to create a ZetaTheme. -class ZetaThemeData { - /// Font family. - /// - /// If null, defaults to `packages/zeta_flutter/IBMPlexSans`; - final String? fontFamily; - - /// Color scheme. - /// - /// If null, defaults to `ZetaColors().toColorScheme`. - final ColorScheme? colorScheme; - - /// ZetaColors - /// - /// If null, defaults to `ZetaColors()` - which builds a light mode color palette with default Zeta Colors. - final ZetaColors? zetaColors; - - /// TextTheme. If null, defaults to [ZetaText.textTheme]. - final TextTheme? textTheme; - - /// Constructs a [ZetaThemeData]. - const ZetaThemeData({ - this.fontFamily, - this.colorScheme, - this.zetaColors, - this.textTheme, - }); - - /// Returns a new [ZetaThemeData] with fields replaced with the passed parameters. - ZetaThemeData copyWith({ - String? fontFamily, - ColorScheme? colorScheme, - ZetaColors? zetaColors, - TextTheme? textTheme, - }) { - return ZetaThemeData( - fontFamily: fontFamily ?? this.fontFamily, - colorScheme: colorScheme ?? this.colorScheme, - zetaColors: zetaColors ?? this.zetaColors, - textTheme: textTheme ?? this.textTheme, - ); - } -} - -/// Font family for whole theme. -extension FontFamily on ThemeData { - /// Default font family used by theme. - /// - /// We assume that the same font should be used for all text styles in a theme, therefore we can extract from any child theme. - String? get fontFamily => textTheme.bodyMedium?.fontFamily; - - /// Default text color used by theme. - /// - /// We assume that the same font should be used for all text styles in a theme, therefore we can extract from any child theme. - Color? get defaultColor => textTheme.bodyMedium?.color; -} diff --git a/lib/src/theme/theme_data.dart b/lib/src/theme/theme_data.dart new file mode 100644 index 00000000..e550c27e --- /dev/null +++ b/lib/src/theme/theme_data.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; + +import 'color_extensions.dart'; +import 'colors.dart'; +import 'constants.dart'; +import 'contrast.dart'; + +export 'breakpoints.dart'; +export 'colors.dart'; +export 'colors_base.dart'; +export 'constants.dart'; + +/// A representation of the Zeta theme data. +/// +/// This class encapsulates the colors and fonts used for the Zeta theme in both light and dark modes. +@immutable +class ZetaThemeData { + /// The font family used in the Zeta theme. + /// + /// Defaults to [kZetaFontFamily] if not provided. + final String fontFamily; + + /// An Identifier cab be assigned to identify the theme uniquely. + /// + /// It can be useful in case selected theme need to be displayed. + /// + /// Defaults to 'default'. + final String identifier; + + final ZetaColors _colorsLight; + + /// The colors used for the light mode of the Zeta theme. + /// + /// Defaults to a light mode color palette with default Zeta colors if not explicitly provided. + ZetaColors get colorsLight => _colorsLight; + + final ZetaColors _colorsDark; + + /// The colors used for the dark mode of the Zeta theme. + /// + /// Defaults to a dark mode color palette with default Zeta colors if not explicitly provided. + ZetaColors get colorsDark => _colorsDark; + + /// Constructs a [ZetaThemeData]. + /// + /// If [primary] and/or [secondary] colors are provided, they will be used to create the light and dark Zeta color palettes. + ZetaThemeData({ + this.fontFamily = kZetaFontFamily, + this.identifier = 'default', + ZetaContrast contrast = ZetaContrast.aa, + ZetaColors? colorsLight, + ZetaColors? colorsDark, + Color? primary, + Color? secondary, + }) : _colorsDark = (primary != null + ? ZetaColors.dark( + contrast: contrast, + primary: primary.zetaColorSwatch, + secondary: secondary?.zetaColorSwatch, + ) + : (colorsDark ?? ZetaColors.dark())) + .apply(contrast: contrast), + _colorsLight = (primary != null + ? ZetaColors.light( + contrast: contrast, + primary: primary.zetaColorSwatch, + secondary: secondary?.zetaColorSwatch, + ) + : (colorsLight ?? ZetaColors.light())) + .apply(contrast: contrast); + + /// Applies the given [contrast] to the current [ZetaThemeData] and returns a new [ZetaThemeData] with the updated contrast. + ZetaThemeData apply({ + required ZetaContrast contrast, + }) { + return ZetaThemeData( + contrast: contrast, + identifier: identifier, + fontFamily: fontFamily, + colorsDark: colorsDark, + colorsLight: colorsLight, + ); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is ZetaThemeData && + runtimeType == other.runtimeType && + fontFamily == other.fontFamily && + identifier == other.identifier && + _colorsLight == other._colorsLight && + _colorsDark == other._colorsDark; + + @override + int get hashCode => fontFamily.hashCode ^ identifier.hashCode ^ _colorsLight.hashCode ^ _colorsDark.hashCode; +} diff --git a/lib/src/theme/theme_service.dart b/lib/src/theme/theme_service.dart new file mode 100644 index 00000000..bbc63ee5 --- /dev/null +++ b/lib/src/theme/theme_service.dart @@ -0,0 +1,25 @@ +import 'theme_data.dart'; + +/// `ZetaThemeService` is an abstract class. +/// It provides the structure for loading and saving themes in Zeta application. + +abstract class ZetaThemeService { + /// Loads the application's theme. + /// + /// `loadTheme` is a method to retrieve the current theme data. + /// + /// Returns a `Future` that completes with the `ZetaThemeData` object + /// that represents the current theme. + + Future loadTheme(); + + /// Saves the provided theme data as the application's theme. + /// + /// `saveTheme` is a method used to save the current theme data. + /// + /// Takes a `ZetaThemeData` object that represents the theme to be saved. + /// + /// Returns a `Future` that completes when the theme data has been successfully saved. + + Future saveTheme(ZetaThemeData themeData); +} diff --git a/lib/src/tokens.dart b/lib/src/tokens.dart index 00ad26c4..fdfe7837 100644 --- a/lib/src/tokens.dart +++ b/lib/src/tokens.dart @@ -99,9 +99,6 @@ class Typography { /// Zeta library contains IBM Plex Sans with latin script. /// In the case of non-latin languages, this can be overridden: /// - /// ``` theme: ZetaTheme.zeta.copyWith(fontFamily: ''),``` - // static const String fontFamily = 'packages/zeta_flutter/IBMPlexSans'; - /// Default text size. /// /// Defaults to [Dimensions.s]. diff --git a/lib/src/zeta.dart b/lib/src/zeta.dart index 085224d5..24b92c7a 100644 --- a/lib/src/zeta.dart +++ b/lib/src/zeta.dart @@ -1,116 +1,359 @@ -import 'package:flutter/cupertino.dart'; +import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import '../../zeta_flutter.dart'; +import 'theme/contrast.dart'; +import 'theme/theme_data.dart'; +import 'theme/theme_service.dart'; -/// Wrapper widget for apps using zeta. -/// -/// Please place this widget at the top level of the application. -/// Typically this would be wrapping a [MaterialApp], [WidgetsApp] or [CupertinoApp]. +/// An [InheritedWidget] that provides access to Zeta theme settings. /// -/// Without this widget in the tree, theming and colors may not work. -class Zeta extends StatefulWidget { - /// Base theme for the app. Zeta styles will be applied on top of this. - final ZetaThemeData? theme; +/// It holds information about the current contrast, theme mode, and theme data. +/// The [colors] getter provides the correct color set based on the current theme mode. +class Zeta extends InheritedWidget { + /// The current contrast setting for the app, which can be one of the predefined + /// values in [ZetaContrast]. + final ZetaContrast contrast; + + /// Specifies the theme mode for the app, which determines how the UI is rendered. + /// + /// It can be one of the values: [ThemeMode.system], [ThemeMode.light], or [ThemeMode.dark]. + final ThemeMode themeMode; + + /// Provides the theme data for the app, which contains all the theming information. + final ZetaThemeData themeData; + + /// Internal property to get the system brightness. + /// Used to determine the theme mode when it's set to [ThemeMode.system]. + final Brightness _mediaBrightness; - /// Override for custom colors. - final ZetaColors? colors; + /// Provides the color set based on the current theme mode. + /// + /// It determines the appropriate color set (light or dark) based on the theme mode + /// and system brightness. + ZetaColors get colors { + if (themeMode == ThemeMode.system) { + return _mediaBrightness == Brightness.light ? themeData.colorsLight : themeData.colorsDark; + } else if (themeMode == ThemeMode.light) { + return themeData.colorsLight; + } else { + return themeData.colorsDark; + } + } + + /// Gets the brightness setting for the current theme. + /// + /// If the theme mode is set to 'system', it will return the brightness that ties with the device's system theme setting. + /// If the theme mode is set to 'light', it always returns `Brightness.light`. + /// If neither, it returns `Brightness.dark` by default (i.e., when the theme mode is 'dark'). + Brightness get brightness { + if (themeMode == ThemeMode.system) { + return _mediaBrightness; // Return the current system brightness setting + } else if (themeMode == ThemeMode.light) { + return Brightness.light; // Return the light mode brightness + } else { + return Brightness.dark; // Default: Return the dark mode brightness + } + } - /// Builder for the app. + /// Constructs a [Zeta] widget. /// - /// Returns theme and colors. - final Widget Function(BuildContext, ThemeData, ZetaColors) builder; + /// The [contrast], [themeMode], [themeData], and [child] arguments are required. + const Zeta({ + super.key, + required Brightness mediaBrightness, + required this.contrast, + required this.themeMode, + required this.themeData, + required super.child, + }) : _mediaBrightness = mediaBrightness; - /// Constructor for [Zeta]. - const Zeta({required this.builder, this.theme, this.colors, super.key}); @override - State createState() => ZetaState(); + bool updateShouldNotify(covariant Zeta oldWidget) { + return oldWidget.contrast != contrast || + oldWidget.themeMode != themeMode || + oldWidget.themeData != themeData || + oldWidget._mediaBrightness != _mediaBrightness; + } + + /// Fetches the [Zeta] instance from the provided [context]. + /// + /// It ensures that the context has access to the [Zeta] theming information. + /// Throws a [FlutterError] if the [Zeta] is not found in the widget tree. + static Zeta of(BuildContext context) { + final defaults = context.dependOnInheritedWidgetOfExactType(); + if (defaults != null) { + return defaults; + } else { + throw FlutterError.fromParts( + [ + ErrorDescription('Unable to find Zeta in the widget tree.'), + ErrorHint( + 'Ensure that the context passed to Zeta.of() is a descendant of a ZetaProvider widget. This usually means that ZetaProviderState should be an ancestor of the widget which uses this context.', + ), + ErrorSpacer(), + ErrorDescription('The widget for the context used was:'), + DiagnosticsProperty('widget', context.widget, showName: false), + ErrorSpacer(), + ErrorHint( + 'If you recently changed the type of that widget, or the widget tree, ensure the ZetaProvider widget is still an ancestor.', + ), + ], + ); + } + } + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('theme', theme)); - properties.add(DiagnosticsProperty('colors', colors)); - properties - .add(ObjectFlagProperty.has('builder', builder)); + properties.add(EnumProperty('contrast', contrast)); + properties.add(EnumProperty('themeMode', themeMode)); + properties.add(DiagnosticsProperty('themeData', themeData)); + properties.add(DiagnosticsProperty('colors', colors)); + properties.add(EnumProperty('mediaBrightness', _mediaBrightness)); + properties.add(EnumProperty('brightness', brightness)); } } -/// State for [Zeta]. -class ZetaState extends State { - ZetaColors _colors = ZetaColors(); - late ThemeData _theme; - late ZetaThemeData _zetaTheme; +/// A typedef for the ZetaAppBuilder function which takes [BuildContext], [ZetaThemeData], +/// and [ThemeMode] and returns a [Widget]. +typedef ZetaAppBuilder = Widget Function(BuildContext context, ZetaThemeData themeData, ThemeMode themeMode); - /// Theme used by zeta. - ZetaThemeData get zetaTheme => _zetaTheme; - set zetaTheme(ZetaThemeData value) { - _zetaTheme = value; - setState(() { - _theme = ZetaTheme.builder(initialTheme: zetaTheme); - }); - } +/// A widget that provides Zeta theming and contrast data down the widget tree. +class ZetaProvider extends StatefulWidget with Diagnosticable { + /// Specifies the initial theme mode for the app. + /// + /// It can be one of the values: [ThemeMode.system], [ThemeMode.light], or [ThemeMode.dark]. + /// Defaults to [ThemeMode.system]. + final ThemeMode initialThemeMode; - bool _ready = false; + /// Provides the initial theme data for the app. + /// + /// This contains all the theming information. If not provided, + /// it defaults to a basic [ZetaThemeData] instance. + final ZetaThemeData initialThemeData; - /// Colors for app. + /// Specifies the initial contrast setting for the app. /// - /// Access using `ZetaColors.of(context)`. - ZetaColors get colors => _colors; + /// Defaults to [ZetaContrast.aa]. + final ZetaContrast initialContrast; - /// Sets colors for app. + /// A builder function to construct the widget tree using the provided theming information. /// - /// Access using `ZetaColors.setColors(context, colors)`. + /// It receives the [BuildContext], [ZetaThemeData], and [ThemeMode] as arguments + /// and is expected to return a [Widget]. + final ZetaAppBuilder builder; + + /// A `ZetaThemeService` /// - /// When called, resets top-level key to force UI rebuild of app. - set colors(ZetaColors value) { - if (_colors != value) { - setState(() { - _colors = value; - _theme = ZetaTheme.builder(initialTheme: zetaTheme.copyWith(zetaColors: value)); - }); + /// It provides the structure for loading and saving themes in Zeta application. + final ZetaThemeService? themeService; + + /// Constructs a [ZetaProvider] widget. + /// + /// The [builder] argument is required. The [initialThemeMode], [initialContrast], + /// and [initialThemeData] arguments provide initial values. + ZetaProvider({ + required this.builder, + this.initialThemeMode = ThemeMode.system, + this.initialContrast = ZetaContrast.aa, + this.themeService, + ZetaThemeData? initialThemeData, + super.key, + }) : initialThemeData = initialThemeData ?? ZetaThemeData(); + + @override + State createState() => ZetaProviderState(); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('themeData', initialThemeData)); + properties.add(ObjectFlagProperty.has('builder', builder)); + properties.add(EnumProperty('initialThemeMode', initialThemeMode)); + properties.add(EnumProperty('initialContrast', initialContrast)); + properties.add(DiagnosticsProperty('themeService', themeService)); + } + + /// Retrieves the [ZetaProviderState] from the provided context. + static ZetaProviderState of(BuildContext context) { + final zetaState = context.findAncestorStateOfType(); + if (zetaState != null) { + return zetaState; + } else { + throw FlutterError.fromParts( + [ + ErrorDescription('Unable to find ZetaProviderState in the widget tree.'), + ErrorHint( + 'Ensure that the context passed to ZetaProvider.of() is a descendant of a ZetaProvider widget. This usually means that ZetaProviderState should be an ancestor of the widget which uses this context.', + ), + ErrorSpacer(), + ErrorDescription('The widget for the context used was:'), + DiagnosticsProperty('widget', context.widget, showName: false), + ErrorSpacer(), + ErrorHint( + 'If you recently changed the type of that widget, or the widget tree, ensure the ZetaProvider widget is still an ancestor.', + ), + ], + ); } } +} + +/// The state associated with [ZetaProvider]. +class ZetaProviderState extends State with Diagnosticable, WidgetsBindingObserver { + // Fields for ZetaThemeManager. + + /// Represents the late initialization of the ZetaContrast value. + late ZetaContrast _contrast; + /// Represents the late initialization of the ThemeMode value. + late ThemeMode _themeMode; + + /// Represents the late initialization of the ZetaThemeData object. + late ZetaThemeData _themeData; + + /// Represents the late initialization of the system's current brightness (dark or light mode). + late Brightness _platformBrightness; + + /// Represents a nullable brightness value to be used for brightness change debouncing. + Brightness? _debounceBrightness; + + /// Timer used for debouncing brightness changes. + Timer? _debounceTimer; + + /// Represents the duration for the debounce timer. + static const _debounceDuration = Duration(milliseconds: 500); + + /// This method is called when this object is inserted into the tree. + /// + /// Here, it also adds this object as an observer in [WidgetsBinding] instance + /// and initializes various fields related to the theme, contrast, and brightness of the app. @override void initState() { super.initState(); + WidgetsBinding.instance.addObserver(this); + + // Set the initial brightness with the system's current brightness from the first view of the platform dispatcher. + _platformBrightness = MediaQueryData.fromView(PlatformDispatcher.instance.views.first).platformBrightness; + + // Set the initial theme mode. + _themeMode = widget.initialThemeMode; + + // Set the initial contrast. + _contrast = widget.initialContrast; + + // Apply the initial contrast to the theme data. + _themeData = widget.initialThemeData.apply(contrast: _contrast); - zetaTheme = (widget.theme ?? const ZetaThemeData()).copyWith(zetaColors: colors); - colors = widget.colors ?? ZetaColors(); - _ready = true; + // Load theme data from themeService if available. + unawaited( + widget.themeService?.loadTheme().then((value) { + if (value != null) { + setState(() { + _themeData = value; + }); + } + }), + ); } + /// Clean up function to be called when this object is removed from the tree. + /// + /// This also removes this object as an observer from the [WidgetsBinding] instance. @override - void didUpdateWidget(Zeta oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.colors != widget.colors) { - colors = widget.colors ?? ZetaColors(); - zetaTheme = (widget.theme ?? const ZetaThemeData()).copyWith(zetaColors: colors); - } - if (oldWidget.theme != widget.theme) { - zetaTheme = (widget.theme ?? const ZetaThemeData()).copyWith(zetaColors: colors); + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + /// Overrides the [didChangePlatformBrightness] method from the parent class. + /// + /// This method gets information about the platform's brightness and updates the app if it ever changes. + /// The changes are debounced with a timer to avoid them being too frequent. + @override + void didChangePlatformBrightness() { + super.didChangePlatformBrightness(); + + // Get the platform brightness from the first view of the platform dispatcher + // `_debounceBrightness` will then hold the new brightness + _debounceBrightness = MediaQueryData.fromView(PlatformDispatcher.instance.views.first).platformBrightness; + + // If the current stored brightness value is different from the newly fetched value + if (_platformBrightness != _debounceBrightness) { + // If brightness has changed, cancel the existing timer and start a new one + + // Cancel existing timer if any + _debounceTimer?.cancel(); + + // Start a new timer with `_debounceDuration` delay + _debounceTimer = Timer(_debounceDuration, () { + // Once timer fires, check if brightness is still different and not null + if (_debounceBrightness != null && _platformBrightness != _debounceBrightness) { + // If brightness value has indeed changed, update the state + setState(() { + // Set the new brightness value + _platformBrightness = _debounceBrightness!; + }); + } + }); } } @override Widget build(BuildContext context) { - if (_ready) { - return Theme( - data: _theme, - child: Builder( - builder: (context) => widget.builder(context, _theme, colors), - ), - ); + return Zeta( + themeMode: _themeMode, + themeData: _themeData, + contrast: _contrast, + mediaBrightness: _platformBrightness, + child: widget.builder(context, _themeData, _themeMode), + ); + } + + @override + void didUpdateWidget(ZetaProvider oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.initialContrast != widget.initialContrast || + oldWidget.initialThemeMode != widget.initialThemeMode || + oldWidget.initialThemeData != widget.initialThemeData) { + setState(() { + _themeMode = widget.initialThemeMode; + _contrast = widget.initialContrast; + _themeData = widget.initialThemeData.apply(contrast: _contrast); + }); } + } - return const SizedBox(); + /// Updates the current theme mode. + void updateThemeMode(ThemeMode themeMode) { + setState(() { + _themeMode = themeMode; + }); + } + + /// Updates the current theme data. + void updateThemeData(ZetaThemeData data) { + setState(() { + _themeData = data.apply(contrast: _contrast); + unawaited(widget.themeService?.saveTheme(data)); + }); + } + + /// Updates the current contrast. + void updateContrast(ZetaContrast contrast) { + setState(() { + _contrast = contrast; + _themeData = _themeData.apply(contrast: contrast); + }); } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('colors', colors)); - properties.add(DiagnosticsProperty('zetaTheme', zetaTheme)); + properties.add(DiagnosticsProperty('themeData', _themeData)); + properties.add(EnumProperty('contrast', _contrast)); + properties.add(EnumProperty('themeMode', _themeMode)); } } diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index b657f5bc..eecb1b7a 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -4,8 +4,12 @@ library zeta_flutter; export 'src/components/grid.dart'; export 'src/components/spacing.dart'; export 'src/components/text.dart'; +export 'src/theme/color_extensions.dart'; +export 'src/theme/color_scheme.dart'; +export 'src/theme/color_swatch.dart'; export 'src/theme/constants.dart'; -export 'src/theme/theme.dart'; +export 'src/theme/contrast.dart'; +export 'src/theme/theme_data.dart'; export 'src/tokens.dart'; export 'src/utils/extensions.dart'; export 'src/zeta.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index a903adee..61acfb32 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: zeta_flutter -version: 0.0.1+12 +version: 0.0.1+13 description: Zebra Design System (Zeta) - Flutter Component Library homepage: https://github.com/zebradevs/zeta_flutter repository: https://github.com/zebradevs/zeta_flutter From ceeeff81ce2546972db59d3bdcf4009ba936e9a8 Mon Sep 17 00:00:00 2001 From: Luke Walton Date: Fri, 10 Nov 2023 09:50:18 +0000 Subject: [PATCH 3/6] Set up with ZDS-analysis (#2) --- .gitignore | 139 ++- .pubignore | 1 - LICENSE-3RD-PARTY | 8 +- all_lint_rules.yaml | 223 ---- analysis_options.yaml | 42 +- android/.gitignore | 9 - .../plugins/GeneratedPluginRegistrant.java | 23 - android/build.gradle | 46 - android/settings.gradle | 1 - android/src/main/AndroidManifest.xml | 3 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - example/macos/Podfile.lock | 2 +- example/pubspec.lock | 369 ------- example/pubspec.yaml | 4 +- ios/.gitignore | 38 - ios/Assets/.gitkeep | 0 ios/Classes/ZetaFlutterPlugin.swift | 14 - ios/Classes/ZetaPlugin.swift | 14 - ios/zeta.podspec | 23 - ios/zeta_flutter.podspec | 23 - lib/src/components/grid.dart | 35 +- lib/src/components/spacing.dart | 38 +- lib/src/components/text.dart | 958 +++++++++--------- lib/src/theme/color_scheme.dart | 5 +- lib/src/theme/color_swatch.dart | 72 +- lib/src/theme/colors.dart | 284 +++--- lib/src/theme/theme_data.dart | 52 +- lib/src/zeta.dart | 81 +- pubspec.yaml | 5 +- 30 files changed, 903 insertions(+), 1625 deletions(-) delete mode 100644 all_lint_rules.yaml delete mode 100644 android/.gitignore delete mode 100644 android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java delete mode 100644 android/build.gradle delete mode 100644 android/settings.gradle delete mode 100644 android/src/main/AndroidManifest.xml delete mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 example/pubspec.lock delete mode 100644 ios/.gitignore delete mode 100644 ios/Assets/.gitkeep delete mode 100644 ios/Classes/ZetaFlutterPlugin.swift delete mode 100644 ios/Classes/ZetaPlugin.swift delete mode 100644 ios/zeta.podspec delete mode 100644 ios/zeta_flutter.podspec diff --git a/.gitignore b/.gitignore index f8ef7c9c..786410e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -# Miscellaneous *.class *.log *.pyc @@ -9,27 +8,141 @@ .history .svn/ migrate_working_dir/ - -# IntelliJ related *.iml *.ipr *.iws .idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. .vscode/settings.json - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. /pubspec.lock **/doc/api/ .dart_tool/ .packages build/ - -.vscode/settings.json .flutter-plugins .flutter-plugins-dependencies -**/.fvm/ \ No newline at end of file +**/.fvm/ +.gradle/ +local.properties +*.log +captures/ +.externalNativeBuild/ +.cxx/ +*.apk +output.json +misc.xml +deploymentTargetDropDown.xml +render.experimental.xml +*.jks +*.keystore +google-services.json +*.hprof +gen-external-apklibs +Pods/ +pubspec.lock +doc/api/ +.env* +*.dart.js +*.info.json +*.js_ +*.js.deps +*.js.map +.env +.fvm/flutter_sdk +.pub-cache/ +.pub/ +coverage/ +lib/generated_plugin_registrant.dart +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/key.properties +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/.last_build_id +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +*.asp +*.cer +*.csr +*.css +*.htm +*.html +*.js +*.jsp +*.php +*.rss +*.wasm +*.wat +*.xhtml +xcuserdata/ +*.xcscmblueprint +*.xccheckout +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcodeproj/project.xcworkspace/ +!*.xcworkspace/contents.xcworkspacedata +/*.gcno +**/xcshareddata/WorkspaceSettings.xcsettings +.AppleDouble +.LSOverride +Icon +._* +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +*.icloud +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db +*.stackdump +[Dd]esktop.ini +$RECYCLE.BIN/ +*.cab +*.msi +*.msix +*.msm +*.msp +*.lnk +**/Generated.xcconfig diff --git a/.pubignore b/.pubignore index 6c34b38e..33e41fc7 100644 --- a/.pubignore +++ b/.pubignore @@ -1,2 +1 @@ -example/ custom_docs/ \ No newline at end of file diff --git a/LICENSE-3RD-PARTY b/LICENSE-3RD-PARTY index 004ba20a..75e911fe 100644 --- a/LICENSE-3RD-PARTY +++ b/LICENSE-3RD-PARTY @@ -1,9 +1,11 @@ ------------------------------------------------------------------------------ +--- + MIT - applies to: + applies to: - tinycolor SIL Open Font License, Version 1.1 applies to: - IBMPlexSans ------------------------------------------------------------------------------ \ No newline at end of file + +--- diff --git a/all_lint_rules.yaml b/all_lint_rules.yaml deleted file mode 100644 index bf6b1ae4..00000000 --- a/all_lint_rules.yaml +++ /dev/null @@ -1,223 +0,0 @@ -linter: - rules: - - always_use_package_imports - - avoid_dynamic_calls - - avoid_empty_else - - avoid_print - - avoid_relative_lib_imports - - avoid_returning_null_for_future - - avoid_slow_async_io - - avoid_type_to_string - - avoid_types_as_parameter_names - - avoid_web_libraries_in_flutter - - cancel_subscriptions - - close_sinks - - collection_methods_unrelated_type - - comment_references - - control_flow_in_finally - - deprecated_member_use_from_same_package - - diagnostic_describe_all_properties - - discarded_futures - - empty_statements - - hash_and_equals - - implicit_reopen - - invalid_case_patterns - - iterable_contains_unrelated_type - - list_remove_unrelated_type - - literal_only_boolean_expressions - - no_adjacent_strings_in_list - - no_duplicate_case_values - - no_logic_in_create_state - - no_self_assignments - - no_wildcard_variable_uses - - prefer_relative_imports - - prefer_void_to_null - - test_types_in_equals - - throw_in_finally - - unnecessary_statements - - unrelated_type_equality_checks - - unsafe_html - - use_build_context_synchronously - - use_key_in_widget_constructors - - valid_regexps - - depend_on_referenced_packages - - package_names - - secure_pubspec_urls - - sort_pub_dependencies - - always_declare_return_types - - always_put_control_body_on_new_line - - always_put_required_named_parameters_first - - always_require_non_null_named_parameters - - always_specify_types - - annotate_overrides - - avoid_annotating_with_dynamic - - avoid_bool_literals_in_conditional_expressions - - avoid_catches_without_on_clauses - - avoid_catching_errors - - avoid_classes_with_only_static_members - - avoid_double_and_int_checks - - avoid_equals_and_hash_code_on_mutable_classes - - avoid_escaping_inner_quotes - - avoid_field_initializers_in_const_classes - - avoid_final_parameters - - avoid_function_literals_in_foreach_calls - - avoid_implementing_value_types - - avoid_init_to_null - - avoid_js_rounded_ints - - avoid_multiple_declarations_per_line - - avoid_null_checks_in_equality_operators - - avoid_positional_boolean_parameters - - avoid_private_typedef_functions - - avoid_redundant_argument_values - - avoid_renaming_method_parameters - - avoid_return_types_on_setters - - avoid_returning_null - - avoid_returning_null_for_void - - avoid_returning_this - - avoid_setters_without_getters - - avoid_shadowing_type_parameters - - avoid_single_cascade_in_expression_statements - - avoid_types_on_closure_parameters - - avoid_unnecessary_containers - - avoid_unused_constructor_parameters - - avoid_void_async - - await_only_futures - - camel_case_extensions - - camel_case_types - - cascade_invocations - - cast_nullable_to_non_nullable - - combinators_ordering - - conditional_uri_does_not_exist - - constant_identifier_names - - curly_braces_in_flow_control_structures - - dangling_library_doc_comments - - deprecated_consistency - - directives_ordering - - do_not_use_environment - - empty_catches - - empty_constructor_bodies - - eol_at_end_of_file - - exhaustive_cases - - file_names - - flutter_style_todos - - implementation_imports - - implicit_call_tearoffs - - join_return_with_assignment - - leading_newlines_in_multiline_strings - - library_annotations - - library_names - - library_prefixes - - library_private_types_in_public_api - - lines_longer_than_80_chars - - matching_super_parameters - - missing_whitespace_between_adjacent_strings - - no_default_cases - - no_leading_underscores_for_library_prefixes - - no_leading_underscores_for_local_identifiers - - no_literal_bool_comparisons - - no_runtimeType_toString - - non_constant_identifier_names - - noop_primitive_operations - - null_check_on_nullable_type_parameter - - null_closures - - omit_local_variable_types - - one_member_abstracts - - only_throw_errors - - overridden_fields - - package_api_docs - - package_prefixed_library_names - - parameter_assignments - - prefer_adjacent_string_concatenation - - prefer_asserts_in_initializer_lists - - prefer_asserts_with_message - - prefer_collection_literals - - prefer_conditional_assignment - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_const_literals_to_create_immutables - - prefer_constructors_over_static_methods - - prefer_contains - - prefer_double_quotes - - prefer_expression_function_bodies - - prefer_final_fields - - prefer_final_in_for_each - - prefer_final_locals - - prefer_final_parameters - - prefer_for_elements_to_map_fromIterable - - prefer_foreach - - prefer_function_declarations_over_variables - - prefer_generic_function_type_aliases - - prefer_if_elements_to_conditional_expressions - - prefer_if_null_operators - - prefer_initializing_formals - - prefer_inlined_adds - - prefer_int_literals - - prefer_interpolation_to_compose_strings - - prefer_is_empty - - prefer_is_not_empty - - prefer_is_not_operator - - prefer_iterable_whereType - - prefer_mixin - - prefer_null_aware_method_calls - - prefer_null_aware_operators - - prefer_single_quotes - - prefer_spread_collections - - prefer_typing_uninitialized_variables - - provide_deprecation_message - - public_member_api_docs - - recursive_getters - - require_trailing_commas - - sized_box_for_whitespace - - sized_box_shrink_expand - - slash_for_doc_comments - - sort_child_properties_last - - sort_constructors_first - - sort_unnamed_constructors_first - - tighten_type_of_initializing_formals - - type_annotate_public_apis - - type_init_formals - - type_literal_in_constant_pattern - - unawaited_futures - - unnecessary_await_in_return - - unnecessary_brace_in_string_interps - - unnecessary_breaks - - unnecessary_const - - unnecessary_constructor_name - - unnecessary_final - - unnecessary_getters_setters - - unnecessary_lambdas - - unnecessary_late - - unnecessary_library_directive - - unnecessary_new - - unnecessary_null_aware_assignments - - unnecessary_null_aware_operator_on_extension_on_nullable - - unnecessary_null_checks - - unnecessary_null_in_if_null_operators - - unnecessary_nullable_for_final_variable_declarations - - unnecessary_overrides - - unnecessary_parenthesis - - unnecessary_raw_strings - - unnecessary_string_escapes - - unnecessary_string_interpolations - - unnecessary_this - - unnecessary_to_list_in_spreads - - unreachable_from_main - - use_colored_box - - use_decorated_box - - use_enums - - use_full_hex_values_for_flutter_colors - - use_function_type_syntax_for_parameters - - use_if_null_to_convert_nulls_to_bools - - use_is_even_rather_than_modulo - - use_late_for_private_fields_and_variables - - use_named_constants - - use_raw_strings - - use_rethrow_when_possible - - use_setters_to_change_properties - - use_string_buffers - - use_string_in_part_of_directives - - use_super_parameters - - use_test_throws_matchers - - use_to_and_as_if_applicable - - void_checks diff --git a/analysis_options.yaml b/analysis_options.yaml index b689dcb6..6bfc3f6f 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,41 +1 @@ -include: all_lint_rules.yaml -analyzer: - exclude: - - "**/*.g.dart" - - "**/*.freezed.dart" - - "test/.test_coverage.dart" - - "bin/cache/**" - - "lib/generated_plugin_registrant.dart" - - language: - strict-casts: true - strict-inference: true - strict-raw-types: true - - errors: - included_file_warning: ignore - missing_required_param: error - missing_return: error - deprecated_member_use_from_same_package: ignore - parameter_assignments: warning - -linter: - rules: - always_put_control_body_on_new_line: false - always_put_required_named_parameters_first: false - always_specify_types: false - always_use_package_imports: false - avoid_classes_with_only_static_members: false - avoid_positional_boolean_parameters: false - avoid_types_on_closure_parameters: false - cascade_invocations: false - close_sinks: false - lines_longer_than_80_chars: false - omit_local_variable_types: false - prefer_constructors_over_static_methods: false - prefer_double_quotes: false - prefer_expression_function_bodies: false - prefer_final_parameters: false - prefer_int_literals: false - sort_constructors_first: false - unnecessary_final: false +include: package:zds_analysis/analysis_options_lib.yaml diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index 161bdcda..00000000 --- a/android/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures -.cxx diff --git a/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java deleted file mode 100644 index d007606a..00000000 --- a/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.flutter.plugins; - -import io.flutter.plugin.common.PluginRegistry; - -/** - * Generated file. Do not edit. - */ -public final class GeneratedPluginRegistrant { - public static void registerWith(PluginRegistry registry) { - if (alreadyRegisteredWith(registry)) { - return; - } - } - - private static boolean alreadyRegisteredWith(PluginRegistry registry) { - final String key = GeneratedPluginRegistrant.class.getCanonicalName(); - if (registry.hasPlugin(key)) { - return true; - } - registry.registrarFor(key); - return false; - } -} diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index 667340b6..00000000 --- a/android/build.gradle +++ /dev/null @@ -1,46 +0,0 @@ -group 'com.zebra.zeta.zeta' -version '1.0-SNAPSHOT' - -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion 31 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - minSdkVersion 16 - } -} diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index c60fa759..00000000 --- a/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'zeta' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml deleted file mode 100644 index ab297369..00000000 --- a/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5..00000000 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5..00000000 --- a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index f632ebd4..0782b5c6 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -20,4 +20,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.12.1 +COCOAPODS: 1.13.0 diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index 21e83efb..00000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,369 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 - url: "https://pub.dev" - source: hosted - version: "1.17.2" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - device_frame: - dependency: transitive - description: - name: device_frame - sha256: afe76182aec178d171953d9b4a50a43c57c7cf3c77d8b09a48bf30c8fa04dd9d - url: "https://pub.dev" - source: hosted - version: "1.1.0" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d - url: "https://pub.dev" - source: hosted - version: "2.4.1" - go_router: - dependency: "direct main" - description: - name: go_router - sha256: bd7e671d26fd39c78cba82070fa34ef1f830b0e7ed1aeebccabc6561302a7ee5 - url: "https://pub.dev" - source: hosted - version: "6.5.9" - google_fonts: - dependency: "direct main" - description: - name: google_fonts - sha256: "2776c66b3e97c6cdd58d1bd3281548b074b64f1fd5c8f82391f7456e38849567" - url: "https://pub.dev" - source: hosted - version: "4.0.5" - http: - dependency: transitive - description: - name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 - url: "https://pub.dev" - source: hosted - version: "4.8.1" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" - url: "https://pub.dev" - source: hosted - version: "0.12.16" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" - url: "https://pub.dev" - source: hosted - version: "0.5.0" - meta: - dependency: transitive - description: - name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - path: - dependency: transitive - description: - name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.dev" - source: hosted - version: "1.8.3" - path_provider: - dependency: transitive - description: - name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa - url: "https://pub.dev" - source: hosted - version: "2.1.1" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" - url: "https://pub.dev" - source: hosted - version: "2.3.1" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - platform: - dependency: transitive - description: - name: platform - sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" - url: "https://pub.dev" - source: hosted - version: "3.1.3" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d - url: "https://pub.dev" - source: hosted - version: "2.1.6" - resizable_widget: - dependency: transitive - description: - name: resizable_widget - sha256: db2919754b93f386b9b3fb15e9f48f6c9d6d41f00a24397629133c99df86606a - url: "https://pub.dev" - source: hosted - version: "1.0.5" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" - source: hosted - version: "1.11.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" - url: "https://pub.dev" - source: hosted - version: "0.6.0" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" - widgetbook: - dependency: "direct dev" - description: - name: widgetbook - sha256: dce7255b085d84ff1c5a941ce581d3bf24edd742902c22b4405279e5c3e22501 - url: "https://pub.dev" - source: hosted - version: "3.3.0" - win32: - dependency: transitive - description: - name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" - url: "https://pub.dev" - source: hosted - version: "5.0.9" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" - url: "https://pub.dev" - source: hosted - version: "1.0.3" - zeta_flutter: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.0.1+13" -sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=3.7.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 54a3fc12..f6c712a9 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -9,8 +9,8 @@ environment: dependencies: flutter: sdk: flutter - go_router: ^6.5.5 - google_fonts: ^4.0.3 + go_router: ^11.1.2 + google_fonts: ^6.1.0 zeta_flutter: path: ../ diff --git a/ios/.gitignore b/ios/.gitignore deleted file mode 100644 index 0c885071..00000000 --- a/ios/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -.idea/ -.vagrant/ -.sconsign.dblite -.svn/ - -.DS_Store -*.swp -profile - -DerivedData/ -build/ -GeneratedPluginRegistrant.h -GeneratedPluginRegistrant.m - -.generated/ - -*.pbxuser -*.mode1v3 -*.mode2v3 -*.perspectivev3 - -!default.pbxuser -!default.mode1v3 -!default.mode2v3 -!default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - -/Flutter/Generated.xcconfig -/Flutter/ephemeral/ -/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/ios/Classes/ZetaFlutterPlugin.swift b/ios/Classes/ZetaFlutterPlugin.swift deleted file mode 100644 index 8744bdc1..00000000 --- a/ios/Classes/ZetaFlutterPlugin.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Flutter -import UIKit - -public class ZetaFlutterPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "zeta_flutter", binaryMessenger: registrar.messenger()) - let instance = ZetaFlutterPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - result("iOS " + UIDevice.current.systemVersion) - } -} diff --git a/ios/Classes/ZetaPlugin.swift b/ios/Classes/ZetaPlugin.swift deleted file mode 100644 index b8639360..00000000 --- a/ios/Classes/ZetaPlugin.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Flutter -import UIKit - -public class ZetaPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "zeta", binaryMessenger: registrar.messenger()) - let instance = ZetaPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - result("iOS " + UIDevice.current.systemVersion) - } -} diff --git a/ios/zeta.podspec b/ios/zeta.podspec deleted file mode 100644 index 20d99c30..00000000 --- a/ios/zeta.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint zeta.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'zeta' - s.version = '0.0.1' - s.summary = 'A new Flutter plugin project.' - s.description = <<-DESC -A new Flutter plugin project. - DESC - s.homepage = 'http://example.com' - s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'Flutter' - s.platform = :ios, '9.0' - - # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.swift_version = '5.0' -end diff --git a/ios/zeta_flutter.podspec b/ios/zeta_flutter.podspec deleted file mode 100644 index db2ecf5e..00000000 --- a/ios/zeta_flutter.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint zeta_flutter.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'zeta_flutter' - s.version = '0.0.1' - s.summary = 'A new Flutter plugin project.' - s.description = <<-DESC -A new Flutter plugin project. - DESC - s.homepage = 'http://example.com' - s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'Flutter' - s.platform = :ios, '9.0' - - # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.swift_version = '5.0' -end diff --git a/lib/src/components/grid.dart b/lib/src/components/grid.dart index ddac4c9f..d41cce71 100644 --- a/lib/src/components/grid.dart +++ b/lib/src/components/grid.dart @@ -38,6 +38,19 @@ extension _Spacing on DeviceType { /// Zeta Grid. class ZetaGrid extends StatelessWidget { + /// Constructs a [ZetaGrid]. + const ZetaGrid({ + required this.children, + this.col = tokens.Grid.defaultCols, + this.noGaps = false, + this.asymmetricWeight, + this.hybrid = false, + super.key, + }) : assert( + asymmetricWeight == null || (asymmetricWeight > 0 && asymmetricWeight < tokens.Grid.defaultCols), + 'If defined, asymmetricWeight should be in the range 1-11', + ); + /// Number of columns in grid. Should be an even number between 2 and 16, although values above 12 should be used sparingly. /// /// Defaults to 12. @@ -65,19 +78,6 @@ class ZetaGrid extends StatelessWidget { /// Defaults to false. final bool hybrid; - /// Constructs a [ZetaGrid]. - const ZetaGrid({ - required this.children, - this.col = tokens.Grid.defaultCols, - this.noGaps = false, - this.asymmetricWeight, - this.hybrid = false, - super.key, - }) : assert( - asymmetricWeight == null || (asymmetricWeight > 0 && asymmetricWeight < tokens.Grid.defaultCols), - 'If defined, asymmetricWeight should be in the range 1-11', - ); - /// Util to return the smaller of 2 values. num returnSmaller(num in1, num in2) { return in1 > in2 ? in2 : in1; @@ -147,9 +147,10 @@ class ZetaGrid extends StatelessWidget { @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('hybrid', hybrid)); - properties.add(DoubleProperty('col', col)); - properties.add(DiagnosticsProperty('noGaps', noGaps)); - properties.add(IntProperty('asymmetricWeight', asymmetricWeight)); + properties + ..add(DiagnosticsProperty('hybrid', hybrid)) + ..add(DoubleProperty('col', col)) + ..add(DiagnosticsProperty('noGaps', noGaps)) + ..add(IntProperty('asymmetricWeight', asymmetricWeight)); } } diff --git a/lib/src/components/spacing.dart b/lib/src/components/spacing.dart index 9762c6fc..f58138d6 100644 --- a/lib/src/components/spacing.dart +++ b/lib/src/components/spacing.dart @@ -51,23 +51,6 @@ enum ZetaSpacingType { /// Zeta Spacing widget. class ZetaSpacing extends StatelessWidget { - static const double _mod = 2; - - /// Child to be wrapped with spacing insets. - final Widget child; - - /// [ZetaSpacingType] insets applied to [child]. - /// - /// Defaults to [ZetaSpacingType.square]. - final ZetaSpacingType? type; - - /// Size of insets to be applied around [child]. - /// - /// Should be an even number, and be no larger than [Dimensions.x24]. - /// - /// Defaults to [Dimensions.x0]. - final double size; - /// Constructs [ZetaSpacing]. const ZetaSpacing( this.child, { @@ -115,6 +98,22 @@ class ZetaSpacing extends StatelessWidget { /// /// {@macro zeta-spacing-inline} const ZetaSpacing.inlineEnd(this.child, {this.size = Dimensions.x0, super.key}) : type = ZetaSpacingType.inlineEnd; + static const double _mod = 2; + + /// Child to be wrapped with spacing insets. + final Widget child; + + /// [ZetaSpacingType] insets applied to [child]. + /// + /// Defaults to [ZetaSpacingType.square]. + final ZetaSpacingType? type; + + /// Size of insets to be applied around [child]. + /// + /// Should be an even number, and be no larger than [Dimensions.x24]. + /// + /// Defaults to [Dimensions.x0]. + final double size; @override Widget build(BuildContext context) { @@ -137,8 +136,9 @@ class ZetaSpacing extends StatelessWidget { @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(EnumProperty('type', type)); - properties.add(DoubleProperty('size', size)); + properties + ..add(EnumProperty('type', type)) + ..add(DoubleProperty('size', size)); } } diff --git a/lib/src/components/text.dart b/lib/src/components/text.dart index ccf44fa8..4132446e 100644 --- a/lib/src/components/text.dart +++ b/lib/src/components/text.dart @@ -13,6 +13,327 @@ import '../tokens.dart' as tokens; /// See also: /// * [Text]. class ZetaText extends StatelessWidget { + /// Constructor for [ZetaText]. + const ZetaText( + this.data, { + this.style, + this.resetHeight = false, + this.textColor, + this.fontSize, + this.maxWidth, + this.fontWeight, + this.fontStyle, + this.upperCase = false, + this.decoration, + this.textDirection = TextDirection.ltr, + this.first = false, + this.last = false, + super.key, + }); + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-body-xs} + ZetaText.bodyXSmall( + this.data, { + this.resetHeight = false, + this.maxWidth, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + super.key, + }) : style = zetaBodyXSmall; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-body-s} + ZetaText.bodySmall( + this.data, { + this.resetHeight = false, + this.maxWidth, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + super.key, + }) : style = zetaBodySmall; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-body-m} + ZetaText.bodyMedium( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaBodyMedium; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-body-l} + ZetaText.bodyLarge( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaBodyLarge; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-label-s} + ZetaText.labelSmall( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + super.key, + this.maxWidth, + }) : style = zetaLabelSmall; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-label-m} + ZetaText.labelMedium( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaLabelMedium; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-label-l} + ZetaText.labelLarge( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaLabelLarge; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-title-s} + ZetaText.titleSmall( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaTitleSmall; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-title-m} + ZetaText.titleMedium( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaTitleMedium; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-title-l} + ZetaText.titleLarge( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaTitleLarge; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-heading-s} + ZetaText.headingSmall( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaHeadingSmall; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-heading-m} + ZetaText.headingMedium( + this.data, { + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.resetHeight = false, + this.maxWidth, + super.key, + }) : style = zetaHeadingMedium; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-heading-l} + ZetaText.headingLarge( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaHeadingLarge; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-display-s} + ZetaText.displaySmall( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaDisplaySmall; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-display-m} + ZetaText.displayMedium( + this.data, { + this.resetHeight = false, + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.maxWidth, + super.key, + }) : style = zetaDisplayMedium; + + /// {@macro zeta-component-text} + /// + /// {@macro zeta-type-display-l} + ZetaText.displayLarge( + this.data, { + this.decoration, + this.fontSize, + this.fontStyle, + this.fontWeight, + this.first = false, + this.last = false, + this.textColor, + this.textDirection = TextDirection.ltr, + this.upperCase = false, + this.resetHeight = false, + this.maxWidth, + super.key, + }) : style = zetaDisplayLarge; static const double _defaultChMultiplier = 66; /// Text styles for Zeta. @@ -240,526 +561,205 @@ class ZetaText extends StatelessWidget { height: tokens.Dimensions.x9 / tokens.Dimensions.x8, ); - /// {@template zeta-type-display-s} - /// Small display text. - /// - /// Used for landing page intros and sections. - /// - /// See also: - /// * [TextTheme.displaySmall]. - /// {@endtemplate} - static TextStyle zetaDisplaySmall = zetaHeadingSmall; - - /// {@template zeta-type-display-m} - /// Medium display text. - /// - /// Used for landing page intros and sections. - /// - /// See also: - /// * [TextTheme.displayMedium]. - /// {@endtemplate} - static TextStyle zetaDisplayMedium = zetaHeadingMedium; - - /// {@template zeta-type-display-l} - /// Large display text. - /// - /// Used for landing page intros. - /// See also: - /// * [TextTheme.displayLarge]. - /// {@endtemplate} - static TextStyle zetaDisplayLarge = zetaHeadingLarge; - - /// Gets approximate char width based on width of O in IBM Plex Sans - /// - /// Only works for IBM Plex. - static double _ch({double multiplier = _defaultChMultiplier, TextStyle? style}) { - final setStyle = style ?? zetaBodyMedium; - const plexCh = 0.6; - - return multiplier * plexCh * (setStyle.fontSize ?? tokens.Dimensions.x3); - } - - /// The text to be displayed. - /// - /// See also: - /// * [Text.data]. - final String? data; - - /// The style applied to the text. - /// - /// Defaults to [zetaBodyMedium]. - /// - /// See also: - /// * [Text.style]. - final TextStyle? style; - - /// Sets text color. - /// - /// See also: - /// * [TextStyle.color]. - final Color? textColor; - - /// Max width of Text box using [_ch]. Not measured in dp / px. - /// - /// [_ch] approximates width of a character using O as basis, so a maxWidth of 60 theoretically returns a max width containing 60 characters. - /// - /// Only works with 'IBM Plex Sans'. - final double? maxWidth; - - /// Font size override. - /// - /// {@template zeta-text-override} - /// Optional as this should be set using [style]. - /// {@endtemplate} - /// See also: - /// * [TextStyle.fontSize]. - final double? fontSize; - - /// Font weight override. - /// - /// {@macro zeta-text-override} - /// - /// See also: - /// * [TextStyle.fontWeight]. - final FontWeight? fontWeight; - - /// Font style override, used to set text to _italic_. - /// - /// - /// - /// See also: - /// * [TextStyle.fontStyle]. - /// * [FontStyle.italic]. - final FontStyle? fontStyle; - - /// Sets all text to uppercase. - /// - /// See also: - /// * [String.toUpperCase]. - final bool upperCase; - - /// Decoration override, used to apply decorations such as underline. + /// {@template zeta-type-display-s} + /// Small display text. /// - /// See also: - /// * [TextDecoration.underline]. - /// * [TextDecoration]. - final TextDecoration? decoration; - - /// Text direction, used to set text to either Left to Right or Right to Left. + /// Used for landing page intros and sections. /// /// See also: - /// * [TextDirection.values]. - final TextDirection textDirection; + /// * [TextTheme.displaySmall]. + /// {@endtemplate} + static TextStyle zetaDisplaySmall = zetaHeadingSmall; - /// Sets padding top to 0. + /// {@template zeta-type-display-m} + /// Medium display text. /// - /// Set to true when this text is first in a list. - final bool first; - - /// Sets padding bottom to 0. + /// Used for landing page intros and sections. /// - /// Set to true when this text is last in a list. - final bool last; + /// See also: + /// * [TextTheme.displayMedium]. + /// {@endtemplate} + static TextStyle zetaDisplayMedium = zetaHeadingMedium; - /// Sets the line height to 1 and spacing to 0. + /// {@template zeta-type-display-l} + /// Large display text. /// - /// Defaults to false. - final bool resetHeight; - - /// Constructor for [ZetaText]. - const ZetaText( - this.data, { - this.style, - this.resetHeight = false, - this.textColor, - this.fontSize, - this.maxWidth, - this.fontWeight, - this.fontStyle, - this.upperCase = false, - this.decoration, - this.textDirection = TextDirection.ltr, - this.first = false, - this.last = false, - super.key, - }); - - EdgeInsets get _padding { - if (resetHeight || (first && last)) return tokens.Dimensions.x0.squish; - - return EdgeInsets.only( - top: first ? tokens.Dimensions.x0 : tokens.Dimensions.x2, - bottom: last ? tokens.Dimensions.x0 : tokens.Dimensions.x2, - ); - } - - double? get _fontSize { - if (fontSize == null) return null; - if (fontSize == tokens.Dimensions.x3_5) { - return tokens.Dimensions.x4 / tokens.Dimensions.x3_5; - } - - return ((fontSize ?? 1) + tokens.Dimensions.x1) / (fontSize ?? 1); - } - - @override - Widget build(BuildContext context) { - TextStyle thisStyle = (style ?? ZetaText.zetaBodyMedium).copyWith( - fontSize: style?.fontSize, - fontWeight: style?.fontWeight, - height: style?.height, - ); - - String data = this.data ?? ''; - final Color color = textColor ?? Zeta.of(context).colors.textDefault; - - thisStyle = thisStyle.copyWith( - fontSize: (fontSize ?? thisStyle.fontSize ?? tokens.Typography.defaultTextSize) * - MediaQuery.of(context).textScaleFactor, - height: _fontSize, - fontWeight: fontWeight, - decoration: decoration ?? TextDecoration.none, - fontStyle: fontStyle, - color: color, - ); - - if (resetHeight) thisStyle = thisStyle.copyWith(height: 1); - if (upperCase) data = data.toUpperCase(); - - return Padding( - padding: _padding, - child: maxWidth == null - ? Text(data, style: thisStyle, textDirection: textDirection) - : Align( - alignment: textDirection == TextDirection.rtl ? Alignment.centerRight : Alignment.centerLeft, - child: SizedBox( - width: maxWidth == null ? null : _ch(multiplier: maxWidth ?? 0, style: thisStyle), - child: Text(data, style: thisStyle, textDirection: textDirection), - ), - ), - ); - } + /// Used for landing page intros. + /// See also: + /// * [TextTheme.displayLarge]. + /// {@endtemplate} + static TextStyle zetaDisplayLarge = zetaHeadingLarge; - /// {@macro zeta-component-text} + /// Gets approximate char width based on width of O in IBM Plex Sans /// - /// {@macro zeta-type-body-xs} - ZetaText.bodyXSmall( - this.data, { - this.resetHeight = false, - this.maxWidth, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - super.key, - }) : style = zetaBodyXSmall; + /// Only works for IBM Plex. + static double _ch({double multiplier = _defaultChMultiplier, TextStyle? style}) { + final setStyle = style ?? zetaBodyMedium; + const plexCh = 0.6; - /// {@macro zeta-component-text} - /// - /// {@macro zeta-type-body-s} - ZetaText.bodySmall( - this.data, { - this.resetHeight = false, - this.maxWidth, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - super.key, - }) : style = zetaBodySmall; + return multiplier * plexCh * (setStyle.fontSize ?? tokens.Dimensions.x3); + } - /// {@macro zeta-component-text} + /// The text to be displayed. /// - /// {@macro zeta-type-body-m} - ZetaText.bodyMedium( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaBodyMedium; + /// See also: + /// * [Text.data]. + final String? data; - /// {@macro zeta-component-text} + /// The style applied to the text. /// - /// {@macro zeta-type-body-l} - ZetaText.bodyLarge( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaBodyLarge; - - /// {@macro zeta-component-text} + /// Defaults to [zetaBodyMedium]. /// - /// {@macro zeta-type-label-s} - ZetaText.labelSmall( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - super.key, - this.maxWidth, - }) : style = zetaLabelSmall; + /// See also: + /// * [Text.style]. + final TextStyle? style; - /// {@macro zeta-component-text} + /// Sets text color. /// - /// {@macro zeta-type-label-m} - ZetaText.labelMedium( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaLabelMedium; + /// See also: + /// * [TextStyle.color]. + final Color? textColor; - /// {@macro zeta-component-text} - /// - /// {@macro zeta-type-label-l} - ZetaText.labelLarge( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaLabelLarge; + /// Max width of Text box using [_ch]. Not measured in dp / px. + /// + /// [_ch] approximates width of a character using O as basis, so a maxWidth of 60 theoretically returns a max width containing 60 characters. + /// + /// Only works with 'IBM Plex Sans'. + final double? maxWidth; - /// {@macro zeta-component-text} + /// Font size override. /// - /// {@macro zeta-type-title-s} - ZetaText.titleSmall( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaTitleSmall; + /// {@template zeta-text-override} + /// Optional as this should be set using [style]. + /// {@endtemplate} + /// See also: + /// * [TextStyle.fontSize]. + final double? fontSize; - /// {@macro zeta-component-text} + /// Font weight override. /// - /// {@macro zeta-type-title-m} - ZetaText.titleMedium( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaTitleMedium; + /// {@macro zeta-text-override} + /// + /// See also: + /// * [TextStyle.fontWeight]. + final FontWeight? fontWeight; - /// {@macro zeta-component-text} + /// Font style override, used to set text to _italic_. /// - /// {@macro zeta-type-title-l} - ZetaText.titleLarge( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaTitleLarge; + /// + /// + /// See also: + /// * [TextStyle.fontStyle]. + /// * [FontStyle.italic]. + final FontStyle? fontStyle; - /// {@macro zeta-component-text} + /// Sets all text to uppercase. /// - /// {@macro zeta-type-heading-s} - ZetaText.headingSmall( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaHeadingSmall; + /// See also: + /// * [String.toUpperCase]. + final bool upperCase; - /// {@macro zeta-component-text} + /// Decoration override, used to apply decorations such as underline. /// - /// {@macro zeta-type-heading-m} - ZetaText.headingMedium( - this.data, { - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.resetHeight = false, - this.maxWidth, - super.key, - }) : style = zetaHeadingMedium; + /// See also: + /// * [TextDecoration.underline]. + /// * [TextDecoration]. + final TextDecoration? decoration; - /// {@macro zeta-component-text} + /// Text direction, used to set text to either Left to Right or Right to Left. /// - /// {@macro zeta-type-heading-l} - ZetaText.headingLarge( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaHeadingLarge; + /// See also: + /// * [TextDirection.values]. + final TextDirection textDirection; - /// {@macro zeta-component-text} + /// Sets padding top to 0. /// - /// {@macro zeta-type-display-s} - ZetaText.displaySmall( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaDisplaySmall; + /// Set to true when this text is first in a list. + final bool first; - /// {@macro zeta-component-text} + /// Sets padding bottom to 0. /// - /// {@macro zeta-type-display-m} - ZetaText.displayMedium( - this.data, { - this.resetHeight = false, - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.maxWidth, - super.key, - }) : style = zetaDisplayMedium; + /// Set to true when this text is last in a list. + final bool last; - /// {@macro zeta-component-text} + /// Sets the line height to 1 and spacing to 0. /// - /// {@macro zeta-type-display-l} - ZetaText.displayLarge( - this.data, { - this.decoration, - this.fontSize, - this.fontStyle, - this.fontWeight, - this.first = false, - this.last = false, - this.textColor, - this.textDirection = TextDirection.ltr, - this.upperCase = false, - this.resetHeight = false, - this.maxWidth, - super.key, - }) : style = zetaDisplayLarge; + /// Defaults to false. + final bool resetHeight; + + EdgeInsets get _padding { + if (resetHeight || (first && last)) return tokens.Dimensions.x0.squish; + + return EdgeInsets.only( + top: first ? tokens.Dimensions.x0 : tokens.Dimensions.x2, + bottom: last ? tokens.Dimensions.x0 : tokens.Dimensions.x2, + ); + } + + double? get _fontSize { + if (fontSize == null) return null; + if (fontSize == tokens.Dimensions.x3_5) { + return tokens.Dimensions.x4 / tokens.Dimensions.x3_5; + } + + return ((fontSize ?? 1) + tokens.Dimensions.x1) / (fontSize ?? 1); + } + + @override + Widget build(BuildContext context) { + TextStyle thisStyle = (style ?? ZetaText.zetaBodyMedium).copyWith( + fontSize: style?.fontSize, + fontWeight: style?.fontWeight, + height: style?.height, + ); + + String data = this.data ?? ''; + final Color color = textColor ?? Zeta.of(context).colors.textDefault; + + thisStyle = thisStyle.copyWith( + fontSize: (fontSize ?? thisStyle.fontSize ?? tokens.Typography.defaultTextSize) * + MediaQuery.of(context).textScaleFactor, + height: _fontSize, + fontWeight: fontWeight, + decoration: decoration ?? TextDecoration.none, + fontStyle: fontStyle, + color: color, + ); + + if (resetHeight) thisStyle = thisStyle.copyWith(height: 1); + if (upperCase) data = data.toUpperCase(); + + return Padding( + padding: _padding, + child: maxWidth == null + ? Text(data, style: thisStyle, textDirection: textDirection) + : Align( + alignment: textDirection == TextDirection.rtl ? Alignment.centerRight : Alignment.centerLeft, + child: SizedBox( + width: maxWidth == null ? null : _ch(multiplier: maxWidth ?? 0, style: thisStyle), + child: Text(data, style: thisStyle, textDirection: textDirection), + ), + ), + ); + } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(StringProperty('data', data)); - properties.add(DiagnosticsProperty('style', style)); - properties.add(ColorProperty('textColor', textColor)); - properties.add(DoubleProperty('maxWidth', maxWidth)); - properties.add(DoubleProperty('fontSize', fontSize)); - properties.add(DiagnosticsProperty('fontWeight', fontWeight)); - properties.add(EnumProperty('fontStyle', fontStyle)); - properties.add(DiagnosticsProperty('upperCase', upperCase)); - properties.add(DiagnosticsProperty('decoration', decoration)); - properties.add(EnumProperty('textDirection', textDirection)); - properties.add(DiagnosticsProperty('first', first)); - properties.add(DiagnosticsProperty('last', last)); - properties.add(DiagnosticsProperty('resetHeight', resetHeight)); + properties + ..add(StringProperty('data', data)) + ..add(DiagnosticsProperty('style', style)) + ..add(ColorProperty('textColor', textColor)) + ..add(DoubleProperty('maxWidth', maxWidth)) + ..add(DoubleProperty('fontSize', fontSize)) + ..add(DiagnosticsProperty('fontWeight', fontWeight)) + ..add(EnumProperty('fontStyle', fontStyle)) + ..add(DiagnosticsProperty('upperCase', upperCase)) + ..add(DiagnosticsProperty('decoration', decoration)) + ..add(EnumProperty('textDirection', textDirection)) + ..add(DiagnosticsProperty('first', first)) + ..add(DiagnosticsProperty('last', last)) + ..add(DiagnosticsProperty('resetHeight', resetHeight)); } } diff --git a/lib/src/theme/color_scheme.dart b/lib/src/theme/color_scheme.dart index ff6d4a92..c8cebf6a 100644 --- a/lib/src/theme/color_scheme.dart +++ b/lib/src/theme/color_scheme.dart @@ -152,8 +152,9 @@ class ZetaColorScheme extends ColorScheme with Diagnosticable { @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('zetaColors', zetaColors)); - properties.add(StringProperty('fontFamily', fontFamily)); + properties + ..add(DiagnosticsProperty('zetaColors', zetaColors)) + ..add(StringProperty('fontFamily', fontFamily)); } @override diff --git a/lib/src/theme/color_swatch.dart b/lib/src/theme/color_swatch.dart index 8401cca8..5b8f8acd 100644 --- a/lib/src/theme/color_swatch.dart +++ b/lib/src/theme/color_swatch.dart @@ -7,12 +7,6 @@ import 'contrast.dart'; /// A swatch of colors with values from 10 (light) to 100 (dark). @immutable class ZetaColorSwatch extends ColorSwatch { - /// Selected contrast level of the system - final Brightness brightness; - - /// Selected contrast level of the system - final ZetaContrast contrast; - /// Constructs a [ZetaColorSwatch]. /// /// See also: @@ -24,6 +18,42 @@ class ZetaColorSwatch extends ColorSwatch { required Map swatch, }) : super(primary, swatch); + /// {@template zeta.color.color_to_swatch} + /// `ZetaColorSwatch` is a color swatch utility to produce different shades + /// of a primary color, following a specific progression of lightness and darkness. + /// + /// This factory constructor creates a color swatch based on a provided primary color. + /// The darker and lighter shades are determined by predefined percentage values. + /// + /// It ensures that the 60th and 80th shades from swatch are abide by the AA and AAA accessibility standards on [background], respectively. + /// [background] color defaults to [ZetaColorBase.greyWarm] shade10. + factory ZetaColorSwatch.fromColor( + Color primary, { + Brightness brightness = Brightness.light, + ZetaContrast contrast = ZetaContrast.aa, + Color background = Colors.white, + }) { + /// Returns a map of colors shades with their respective indexes. + /// Darker shades are obtained by darkening the primary color and + /// lighter shades by lightening it. + /// + /// - 100, 90, 80, and 70 are darker shades of the primary color. + /// - 60 is the primary color itself. + /// - 50, 40, 30, 20, and 10 are progressively lighter shades of the primary color. + return ZetaColorSwatch( + contrast: contrast, + brightness: brightness, + primary: primary.value, + swatch: primary.generateSwatch(background: background), + ).apply(brightness: brightness); + } + + /// Selected contrast level of the system + final Brightness brightness; + + /// Selected contrast level of the system + final ZetaContrast contrast; + /// This method is an override of the index operator. /// /// If the requested index is not in the table (i.e., it results in `null`), the method returns `this`, @@ -73,36 +103,6 @@ class ZetaColorSwatch extends ColorSwatch { /// Color shade(int number) => this[number]!; - /// {@template zeta.color.color_to_swatch} - /// `ZetaColorSwatch` is a color swatch utility to produce different shades - /// of a primary color, following a specific progression of lightness and darkness. - /// - /// This factory constructor creates a color swatch based on a provided primary color. - /// The darker and lighter shades are determined by predefined percentage values. - /// - /// It ensures that the 60th and 80th shades from swatch are abide by the AA and AAA accessibility standards on [background], respectively. - /// [background] color defaults to [ZetaColorBase.greyWarm] shade10. - factory ZetaColorSwatch.fromColor( - Color primary, { - Brightness brightness = Brightness.light, - ZetaContrast contrast = ZetaContrast.aa, - Color background = Colors.white, - }) { - /// Returns a map of colors shades with their respective indexes. - /// Darker shades are obtained by darkening the primary color and - /// lighter shades by lightening it. - /// - /// - 100, 90, 80, and 70 are darker shades of the primary color. - /// - 60 is the primary color itself. - /// - 50, 40, 30, 20, and 10 are progressively lighter shades of the primary color. - return ZetaColorSwatch( - contrast: contrast, - brightness: brightness, - primary: primary.value, - swatch: primary.generateSwatch(background: background), - ).apply(brightness: brightness); - } - /// Returns the color shade for a surface depending on the ZetaContrast value. /// /// For [ZetaContrast.aa], it returns 60. diff --git a/lib/src/theme/colors.dart b/lib/src/theme/colors.dart index fb9b5b20..c54f270f 100644 --- a/lib/src/theme/colors.dart +++ b/lib/src/theme/colors.dart @@ -11,6 +11,148 @@ import 'contrast.dart'; /// A customizable, token-based color palette, adapting Zeta colors to Flutter's colorScheme. @immutable class ZetaColors { + /// Default constructor for instance of [ZetaColors]. + ZetaColors({ + this.brightness = Brightness.light, + this.contrast = ZetaContrast.aa, + this.link = ZetaColorBase.linkLight, + this.linkVisited = ZetaColorBase.linkVisitedLight, + this.shadow = ZetaColorBase.shadowLight, + this.white = ZetaColorBase.white, + this.black = ZetaColorBase.black, + ZetaColorSwatch? primary, + ZetaColorSwatch? secondary, + ZetaColorSwatch? error, + ZetaColorSwatch? cool, + ZetaColorSwatch? warm, + Color? surfacePrimary, + Color? surfaceSecondary, + Color? surfaceTertiary, + bool adjust = true, + }) : primary = _adjustedValue(primary, ZetaColorBase.blue, adjust, brightness, contrast), + secondary = _adjustedValue(secondary, primary ?? ZetaColorBase.blue, adjust, brightness, contrast), + error = _adjustedValue(error, ZetaColorBase.red, adjust, brightness, contrast), + cool = _adjustedValue(cool, ZetaColorBase.greyCool, adjust, brightness, ZetaContrast.aa), + warm = _adjustedValue(warm, ZetaColorBase.greyWarm, adjust, brightness, ZetaContrast.aa), + blue = _adjustedBase(ZetaColorBase.blue, adjust, brightness, contrast), + green = _adjustedBase(ZetaColorBase.green, adjust, brightness, contrast), + red = _adjustedBase(ZetaColorBase.red, adjust, brightness, contrast), + orange = _adjustedBase(ZetaColorBase.orange, adjust, brightness, contrast), + purple = _adjustedBase(ZetaColorBase.purple, adjust, brightness, contrast), + yellow = _adjustedBase(ZetaColorBase.yellow, adjust, brightness, contrast), + teal = _adjustedBase(ZetaColorBase.teal, adjust, brightness, contrast), + pink = _adjustedBase(ZetaColorBase.pink, adjust, brightness, contrast), + surfacePrimary = surfacePrimary ?? white, + surfaceSecondary = surfaceSecondary ?? + _adjustedValue( + cool, + ZetaColorBase.greyCool, + adjust, + brightness, + ZetaContrast.aa, + ).shade10, + surfaceTertiary = surfaceTertiary ?? + _adjustedValue( + warm, + ZetaColorBase.greyWarm, + adjust, + brightness, + ZetaContrast.aa, + ).shade10; + + /// Factory constructor for a light theme for [ZetaColors]. + /// + /// All color options are nullable and default to a pre-defined contrast color if null. + /// + /// [contrast] The primary contrast color. If not supplied, defaults to [ZetaContrast.aa]. + /// [primary] A color swatch for primary color accent. Defaults to null. + /// [secondary] A color swatch for secondary color accent. Defaults to null. + /// [error] A color swatch for error states. Defaults to null. + /// [cool] A color swatch for cooler color tones. Defaults to null. + /// [warm] A color swatch for warmer color tones. Defaults to null. + /// [white] A color option for white color. Defaults to null. + /// [black] A color option for black color. Defaults to null. + /// [link] A color option for links. Defaults to null. + /// [linkVisited] A color option for visited links. Defaults to null. + /// [shadow] A color option for shadows. Defaults to null. + factory ZetaColors.light({ + ZetaContrast contrast = ZetaContrast.aa, + ZetaColorSwatch? primary, + ZetaColorSwatch? secondary, + ZetaColorSwatch? error, + ZetaColorSwatch? cool, + ZetaColorSwatch? warm, + Color? white, + Color? black, + Color? link, + Color? linkVisited, + Color? shadow, + }) { + return ZetaColors( + white: white ?? ZetaColorBase.white, + black: black ?? ZetaColorBase.black, + cool: cool, + warm: warm, + error: error, + primary: primary, + contrast: contrast, + secondary: secondary, + surfaceTertiary: warm?.shade10, + surfaceSecondary: cool?.shade10, + surfacePrimary: white ?? ZetaColorBase.white, + link: link ?? ZetaColorBase.linkLight, + shadow: shadow ?? ZetaColorBase.shadowLight, + linkVisited: linkVisited ?? ZetaColorBase.linkVisitedLight, + ); + } + + /// Factory constructor for a dark theme for [ZetaColors]. + /// + /// All color options are nullable and default to a pre-defined contrast color if null. + /// + /// [contrast] The primary contrast color. If not supplied, defaults to [ZetaContrast.aa]. + /// [primary] A color swatch for primary color accent. Defaults to null. + /// [secondary] A color swatch for secondary color accent. Defaults to null. + /// [error] A color swatch for error states. Defaults to null. + /// [cool] A color swatch for cooler color tones. Defaults to null. + /// [warm] A color swatch for warmer color tones. Defaults to null. + /// [white] A color option for white color. Defaults to null. + /// [black] A color option for black color. Defaults to null. + /// [link] A color option for links. Defaults to null. + /// [linkVisited] A color option for visited links. Defaults to null. + /// [shadow] A color option for shadows. Defaults to null. + factory ZetaColors.dark({ + ZetaContrast contrast = ZetaContrast.aa, + ZetaColorSwatch? primary, + ZetaColorSwatch? secondary, + ZetaColorSwatch? error, + ZetaColorSwatch? cool, + ZetaColorSwatch? warm, + Color? white, + Color? black, + Color? link, + Color? linkVisited, + Color? shadow, + }) { + return ZetaColors( + cool: cool, + warm: warm, + white: white ?? ZetaColorBase.white, + black: black ?? ZetaColorBase.black, + primary: primary, + contrast: contrast, + secondary: secondary, + error: error, + brightness: Brightness.dark, + surfaceTertiary: warm?.shade10, + surfaceSecondary: cool?.shade10, + surfacePrimary: black ?? ZetaColorBase.black, + link: link ?? ZetaColorBase.linkDark, + shadow: shadow ?? ZetaColorBase.shadowLight, + linkVisited: linkVisited ?? ZetaColorBase.linkVisitedDark, + ); + } + /// Represents the brightness value. final Brightness brightness; @@ -336,55 +478,6 @@ class ZetaColors { /// Colorful colors. List get rainbow => [red, orange, yellow, green, blue, teal, pink]; - /// Default constructor for instance of [ZetaColors]. - ZetaColors({ - this.brightness = Brightness.light, - this.contrast = ZetaContrast.aa, - this.link = ZetaColorBase.linkLight, - this.linkVisited = ZetaColorBase.linkVisitedLight, - this.shadow = ZetaColorBase.shadowLight, - this.white = ZetaColorBase.white, - this.black = ZetaColorBase.black, - ZetaColorSwatch? primary, - ZetaColorSwatch? secondary, - ZetaColorSwatch? error, - ZetaColorSwatch? cool, - ZetaColorSwatch? warm, - Color? surfacePrimary, - Color? surfaceSecondary, - Color? surfaceTertiary, - bool adjust = true, - }) : primary = _adjustedValue(primary, ZetaColorBase.blue, adjust, brightness, contrast), - secondary = _adjustedValue(secondary, primary ?? ZetaColorBase.blue, adjust, brightness, contrast), - error = _adjustedValue(error, ZetaColorBase.red, adjust, brightness, contrast), - cool = _adjustedValue(cool, ZetaColorBase.greyCool, adjust, brightness, ZetaContrast.aa), - warm = _adjustedValue(warm, ZetaColorBase.greyWarm, adjust, brightness, ZetaContrast.aa), - blue = _adjustedBase(ZetaColorBase.blue, adjust, brightness, contrast), - green = _adjustedBase(ZetaColorBase.green, adjust, brightness, contrast), - red = _adjustedBase(ZetaColorBase.red, adjust, brightness, contrast), - orange = _adjustedBase(ZetaColorBase.orange, adjust, brightness, contrast), - purple = _adjustedBase(ZetaColorBase.purple, adjust, brightness, contrast), - yellow = _adjustedBase(ZetaColorBase.yellow, adjust, brightness, contrast), - teal = _adjustedBase(ZetaColorBase.teal, adjust, brightness, contrast), - pink = _adjustedBase(ZetaColorBase.pink, adjust, brightness, contrast), - surfacePrimary = surfacePrimary ?? white, - surfaceSecondary = surfaceSecondary ?? - _adjustedValue( - cool, - ZetaColorBase.greyCool, - adjust, - brightness, - ZetaContrast.aa, - ).shade10, - surfaceTertiary = surfaceTertiary ?? - _adjustedValue( - warm, - ZetaColorBase.greyWarm, - adjust, - brightness, - ZetaContrast.aa, - ).shade10; - /// Helper function to adjust color swatch values based on brightness and contrast static ZetaColorSwatch _adjustedValue( ZetaColorSwatch? value, @@ -407,99 +500,6 @@ class ZetaColors { return adjust ? baseColor.apply(brightness: brightness, contrast: contrast) : baseColor; } - /// Factory constructor for a light theme for [ZetaColors]. - /// - /// All color options are nullable and default to a pre-defined contrast color if null. - /// - /// [contrast] The primary contrast color. If not supplied, defaults to [ZetaContrast.aa]. - /// [primary] A color swatch for primary color accent. Defaults to null. - /// [secondary] A color swatch for secondary color accent. Defaults to null. - /// [error] A color swatch for error states. Defaults to null. - /// [cool] A color swatch for cooler color tones. Defaults to null. - /// [warm] A color swatch for warmer color tones. Defaults to null. - /// [white] A color option for white color. Defaults to null. - /// [black] A color option for black color. Defaults to null. - /// [link] A color option for links. Defaults to null. - /// [linkVisited] A color option for visited links. Defaults to null. - /// [shadow] A color option for shadows. Defaults to null. - factory ZetaColors.light({ - ZetaContrast contrast = ZetaContrast.aa, - ZetaColorSwatch? primary, - ZetaColorSwatch? secondary, - ZetaColorSwatch? error, - ZetaColorSwatch? cool, - ZetaColorSwatch? warm, - Color? white, - Color? black, - Color? link, - Color? linkVisited, - Color? shadow, - }) { - return ZetaColors( - white: white ?? ZetaColorBase.white, - black: black ?? ZetaColorBase.black, - cool: cool, - warm: warm, - error: error, - primary: primary, - contrast: contrast, - secondary: secondary, - surfaceTertiary: warm?.shade10, - surfaceSecondary: cool?.shade10, - surfacePrimary: white ?? ZetaColorBase.white, - link: link ?? ZetaColorBase.linkLight, - shadow: shadow ?? ZetaColorBase.shadowLight, - linkVisited: linkVisited ?? ZetaColorBase.linkVisitedLight, - ); - } - - /// Factory constructor for a dark theme for [ZetaColors]. - /// - /// All color options are nullable and default to a pre-defined contrast color if null. - /// - /// [contrast] The primary contrast color. If not supplied, defaults to [ZetaContrast.aa]. - /// [primary] A color swatch for primary color accent. Defaults to null. - /// [secondary] A color swatch for secondary color accent. Defaults to null. - /// [error] A color swatch for error states. Defaults to null. - /// [cool] A color swatch for cooler color tones. Defaults to null. - /// [warm] A color swatch for warmer color tones. Defaults to null. - /// [white] A color option for white color. Defaults to null. - /// [black] A color option for black color. Defaults to null. - /// [link] A color option for links. Defaults to null. - /// [linkVisited] A color option for visited links. Defaults to null. - /// [shadow] A color option for shadows. Defaults to null. - factory ZetaColors.dark({ - ZetaContrast contrast = ZetaContrast.aa, - ZetaColorSwatch? primary, - ZetaColorSwatch? secondary, - ZetaColorSwatch? error, - ZetaColorSwatch? cool, - ZetaColorSwatch? warm, - Color? white, - Color? black, - Color? link, - Color? linkVisited, - Color? shadow, - }) { - return ZetaColors( - cool: cool, - warm: warm, - white: white ?? ZetaColorBase.white, - black: black ?? ZetaColorBase.black, - primary: primary, - contrast: contrast, - secondary: secondary, - error: error, - brightness: Brightness.dark, - surfaceTertiary: warm?.shade10, - surfaceSecondary: cool?.shade10, - surfacePrimary: black ?? ZetaColorBase.black, - link: link ?? ZetaColorBase.linkDark, - shadow: shadow ?? ZetaColorBase.shadowLight, - linkVisited: linkVisited ?? ZetaColorBase.linkVisitedDark, - ); - } - /// Applies new property values to [ZetaColors] and returns a new copy. /// /// Each property defaults to the previous value if not specified. diff --git a/lib/src/theme/theme_data.dart b/lib/src/theme/theme_data.dart index e550c27e..9d95d801 100644 --- a/lib/src/theme/theme_data.dart +++ b/lib/src/theme/theme_data.dart @@ -15,32 +15,6 @@ export 'constants.dart'; /// This class encapsulates the colors and fonts used for the Zeta theme in both light and dark modes. @immutable class ZetaThemeData { - /// The font family used in the Zeta theme. - /// - /// Defaults to [kZetaFontFamily] if not provided. - final String fontFamily; - - /// An Identifier cab be assigned to identify the theme uniquely. - /// - /// It can be useful in case selected theme need to be displayed. - /// - /// Defaults to 'default'. - final String identifier; - - final ZetaColors _colorsLight; - - /// The colors used for the light mode of the Zeta theme. - /// - /// Defaults to a light mode color palette with default Zeta colors if not explicitly provided. - ZetaColors get colorsLight => _colorsLight; - - final ZetaColors _colorsDark; - - /// The colors used for the dark mode of the Zeta theme. - /// - /// Defaults to a dark mode color palette with default Zeta colors if not explicitly provided. - ZetaColors get colorsDark => _colorsDark; - /// Constructs a [ZetaThemeData]. /// /// If [primary] and/or [secondary] colors are provided, they will be used to create the light and dark Zeta color palettes. @@ -69,6 +43,32 @@ class ZetaThemeData { : (colorsLight ?? ZetaColors.light())) .apply(contrast: contrast); + /// The font family used in the Zeta theme. + /// + /// Defaults to [kZetaFontFamily] if not provided. + final String fontFamily; + + /// An Identifier cab be assigned to identify the theme uniquely. + /// + /// It can be useful in case selected theme need to be displayed. + /// + /// Defaults to 'default'. + final String identifier; + + final ZetaColors _colorsLight; + + /// The colors used for the light mode of the Zeta theme. + /// + /// Defaults to a light mode color palette with default Zeta colors if not explicitly provided. + ZetaColors get colorsLight => _colorsLight; + + final ZetaColors _colorsDark; + + /// The colors used for the dark mode of the Zeta theme. + /// + /// Defaults to a dark mode color palette with default Zeta colors if not explicitly provided. + ZetaColors get colorsDark => _colorsDark; + /// Applies the given [contrast] to the current [ZetaThemeData] and returns a new [ZetaThemeData] with the updated contrast. ZetaThemeData apply({ required ZetaContrast contrast, diff --git a/lib/src/zeta.dart b/lib/src/zeta.dart index 24b92c7a..cf8a7c42 100644 --- a/lib/src/zeta.dart +++ b/lib/src/zeta.dart @@ -12,6 +12,18 @@ import 'theme/theme_service.dart'; /// It holds information about the current contrast, theme mode, and theme data. /// The [colors] getter provides the correct color set based on the current theme mode. class Zeta extends InheritedWidget { + /// Constructs a [Zeta] widget. + /// + /// The [contrast], [themeMode], [themeData], and [child] arguments are required. + const Zeta({ + super.key, + required Brightness mediaBrightness, + required this.contrast, + required this.themeMode, + required this.themeData, + required super.child, + }) : _mediaBrightness = mediaBrightness; + /// The current contrast setting for the app, which can be one of the predefined /// values in [ZetaContrast]. final ZetaContrast contrast; @@ -57,18 +69,6 @@ class Zeta extends InheritedWidget { } } - /// Constructs a [Zeta] widget. - /// - /// The [contrast], [themeMode], [themeData], and [child] arguments are required. - const Zeta({ - super.key, - required Brightness mediaBrightness, - required this.contrast, - required this.themeMode, - required this.themeData, - required super.child, - }) : _mediaBrightness = mediaBrightness; - @override bool updateShouldNotify(covariant Zeta oldWidget) { return oldWidget.contrast != contrast || @@ -107,12 +107,13 @@ class Zeta extends InheritedWidget { @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(EnumProperty('contrast', contrast)); - properties.add(EnumProperty('themeMode', themeMode)); - properties.add(DiagnosticsProperty('themeData', themeData)); - properties.add(DiagnosticsProperty('colors', colors)); - properties.add(EnumProperty('mediaBrightness', _mediaBrightness)); - properties.add(EnumProperty('brightness', brightness)); + properties + ..add(EnumProperty('contrast', contrast)) + ..add(EnumProperty('themeMode', themeMode)) + ..add(DiagnosticsProperty('themeData', themeData)) + ..add(DiagnosticsProperty('colors', colors)) + ..add(EnumProperty('mediaBrightness', _mediaBrightness)) + ..add(EnumProperty('brightness', brightness)); } } @@ -122,6 +123,19 @@ typedef ZetaAppBuilder = Widget Function(BuildContext context, ZetaThemeData the /// A widget that provides Zeta theming and contrast data down the widget tree. class ZetaProvider extends StatefulWidget with Diagnosticable { + /// Constructs a [ZetaProvider] widget. + /// + /// The [builder] argument is required. The [initialThemeMode], [initialContrast], + /// and [initialThemeData] arguments provide initial values. + ZetaProvider({ + required this.builder, + this.initialThemeMode = ThemeMode.system, + this.initialContrast = ZetaContrast.aa, + this.themeService, + ZetaThemeData? initialThemeData, + super.key, + }) : initialThemeData = initialThemeData ?? ZetaThemeData(); + /// Specifies the initial theme mode for the app. /// /// It can be one of the values: [ThemeMode.system], [ThemeMode.light], or [ThemeMode.dark]. @@ -150,30 +164,18 @@ class ZetaProvider extends StatefulWidget with Diagnosticable { /// It provides the structure for loading and saving themes in Zeta application. final ZetaThemeService? themeService; - /// Constructs a [ZetaProvider] widget. - /// - /// The [builder] argument is required. The [initialThemeMode], [initialContrast], - /// and [initialThemeData] arguments provide initial values. - ZetaProvider({ - required this.builder, - this.initialThemeMode = ThemeMode.system, - this.initialContrast = ZetaContrast.aa, - this.themeService, - ZetaThemeData? initialThemeData, - super.key, - }) : initialThemeData = initialThemeData ?? ZetaThemeData(); - @override State createState() => ZetaProviderState(); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('themeData', initialThemeData)); - properties.add(ObjectFlagProperty.has('builder', builder)); - properties.add(EnumProperty('initialThemeMode', initialThemeMode)); - properties.add(EnumProperty('initialContrast', initialContrast)); - properties.add(DiagnosticsProperty('themeService', themeService)); + properties + ..add(DiagnosticsProperty('themeData', initialThemeData)) + ..add(ObjectFlagProperty.has('builder', builder)) + ..add(EnumProperty('initialThemeMode', initialThemeMode)) + ..add(EnumProperty('initialContrast', initialContrast)) + ..add(DiagnosticsProperty('themeService', themeService)); } /// Retrieves the [ZetaProviderState] from the provided context. @@ -352,8 +354,9 @@ class ZetaProviderState extends State with Diagnosticable, Widgets @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('themeData', _themeData)); - properties.add(EnumProperty('contrast', _contrast)); - properties.add(EnumProperty('themeMode', _themeMode)); + properties + ..add(DiagnosticsProperty('themeData', _themeData)) + ..add(EnumProperty('contrast', _contrast)) + ..add(EnumProperty('themeMode', _themeMode)); } } diff --git a/pubspec.yaml b/pubspec.yaml index 61acfb32..564d0e43 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: zeta_flutter -version: 0.0.1+13 -description: Zebra Design System (Zeta) - Flutter Component Library +version: 0.1.0+1 +description: Zeta is the new, formal, standardized Zebra Design System based off the successes of ZDS (Zebra Design System). This package is in pre-release, and so many aspects are incomplete. homepage: https://github.com/zebradevs/zeta_flutter repository: https://github.com/zebradevs/zeta_flutter issue_tracker: https://github.com/zebradevs/zeta_flutter/issues @@ -25,6 +25,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + zds_analysis: ^1.0.0 flutter: fonts: From 33b0c7468a50ff7d22b6172fc934affde4025015 Mon Sep 17 00:00:00 2001 From: Luke Walton Date: Tue, 28 Nov 2023 16:12:44 +0000 Subject: [PATCH 4/6] bump version (#6) --- .gitignore | 1 - CHANGELOG.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 786410e2..369fe7d8 100644 --- a/.gitignore +++ b/.gitignore @@ -97,7 +97,6 @@ lib/generated_plugin_registrant.dart *.csr *.css *.htm -*.html *.js *.jsp *.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 71a19d7b..2d554425 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,87 @@ +## [0.1.0+1] - 2023-11-28 + +- chore: Tidy, reorganise and prepare repo + +docs: update changelog and documentation + +feat(type): Add xSmall and conform to latest figma designs. + +- "Refactored the ZetaColors class for better customization + +The ZetaColors class was heavily refactored for better customization of variables such as brightness, contrast, color swatches, and additional color attributes. Several fields were made final for the overall class safety. This change improves color control on different themes for the application." + +- "Updated the ZetaColor and Theme setup to use InheritedWidget + +The ZetaDefaults class was updated to Zeta inheriting from InheritedWidget. This change allows easy access to the Zeta theme settings (contrast, theme mode, theme data, color set) from anywhere in the widget tree. The ZetaAppBuilder function was updated to take in ThemeData and ThemeMode. The ZetaProvider was added to provide Zeta theming and contrast data down the widget tree. The code for the color and typography examples was adjusted to use the new Zeta context extension, instead of using Theme.of(context) to get colorScheme. This change was crucial to simplify the process of adapting the application visuals to different themes." + +- "Improve theme management functionality in Zeta + +Removed code concerning getting a color's RGB hex code from 'color_extensions.dart', as it was seldom used. Updated 'zeta_flutter.dart' to unhide ZetaColorGetters. Adaptations in 'zeta.dart' included switching mediaBrightness to \_mediaBrightness for internal use and adding methods for accurate determination of color set and brightness settings based on the theme mode. Also, ZetaProvider was updated for 'system' theme mode support. example/lib/main.dart and example/lib/widgets.dart were updated to support these changes, including UI updates for seamless theme switching." + +- "Add theme update function and extend ZetaColorGetters + +Implemented a method in 'zeta.dart' to support updating the current theme data dynamically. Extended 'color_scheme.dart' by introducing \_ZetaColorProperties and updating ZetaColorGetters. These changes increase flexibility for theme management and provide a structured and accessible way to get Zeta colors through the theme context." + +- Remove theme_extensions.dart and move contents to colors.dart + +Theme extensions were deleted and its contents were moved to colors.dart to consolidate all color-related codes in one file for easier navigation and editing. Additional enhancements include optimizing color assignments and making ZetaColors immutable for more robust color management. + +- Refactor code for color theme and add theme switcher + +Refactored codebase to improve the color theme handling: relocated theme related methods to colors.dart from theme_extensions.dart for consolidated color theme data. Optimized color assignments by leveraging the 'copyWith' method, allowing more efficient color management. Introduced the immutability of ZetaColors to enhance robustness. Bumped version in pubspec.yaml to 0.0.1+13 due to these changes. Renamed theme.dart to theme_data.dart for more semantic file naming. Added 'identifier' to the ZetaThemeData for easier theme identification. +The visible application change is an added ThemeSwitcher in the example app, offering a UI to switch between different predefined themes. + +- Add ZetaThemeService and theme switcher in example app + +Implemented ZetaThemeService as an abstract class, providing structure for loading and saving themes within the app. Removed an obsolete comment within the contrast.dart and made necessary imports in zeta.dart. Asynchronous theme loading is added during app startup and saving is done upon theme updates. Also, for user-interaction, an exclusive ThemeSwitcher widget is added in the example app allowing users to select between available themes. This improves user experience, and optimizes theme handling and application performance. + +- Refactor color swatch generation to utilize zeta + +Refactored color swatch generation in color_example.dart to use Zeta instead of directly using the Theme. Now the brightness for ZetaColorSwatch is being pulled from zeta object rather than theme. This ensures consistency across different parts of the application where Zeta is used. Also changed theme.colorScheme.surface to colors.surfacePrimary for better readability, and alignment with use of zeta object. + +- Add icon colors to color scheme + +Extended the color scheme in colors.dart to include default, subtle, disabled, and inverse icon colors. These were added to ensure consistent icon colors across the application and support dark mode by allowing inverted color swatches. + +- Refactor theme switch settings and add new features + +Renamed 'theme_switch.dart' to 'theme_color_switch.dart' and added two new files 'theme_contrast_switch.dart' and 'theme_mode_switch.dart' in order to separate the theme settings logically into distinct features - Theme Color Switch, Theme Contrast Switch and Theme Mode Switch respectively. Also, the theme application feature has been refactored within 'widgets.dart' to use the newly created theme features instead of the old theme switch. This enhances modularity and the user's ability to switch theme settings easily. + +- Update method naming for consistent architecture in text.dart + +Changed the method name 'withColor' to 'themeWithColor' in text.dart for consistency with other part of the architecture and for better readability. This change supports the shift towards a consistently designed application architecture and helps developers easily decipher the role of the method in the code. + +- Update color scheme mapping and library version in colors.dart and pubspec.lock + +Refined the color mapping in ZetaColorScheme in colors.dart by replacing effectiveSurfaceTertiary with textDefault, enhancing the clarity of backdrop's color role. Concurrently, version of multiple dependencies in pubspec.lock are updated to benefit from recent fixes and improvements in those libraries. + +- Change `Color` to `ZetaColorSwatch` in theme files + +Adjusted the class references in colors.dart from `Color` to `ZetaColorSwatch` to provide a more consistent color swatch across the app. The swatch allows for more flexibility in using color variations. Adjustments were also made in color_scheme.dart and color_swatch.dart to include better explanatory messages and use standard dart documentation format. Changes in custom_docs/components/Color/flutter.md were made to align with these updates. + +- Enhance contrast and color handling in theme files + +Removed 'flutter.md' as it is no longer required due to improvements made in contrast and color handling. For better accessibility support, 'contrast.dart' was refactored for better contrast handling and 'color_extensions.dart' now includes a mechanism to generate color swatch based on contrast ratio. Also, 'zeta.dart' was updated to adapt to the system's brightness providing better user experience. Overall, these adjustments aim to enhance accessibility and user experience, apart from simplifying the codebase. + +- Add LICENSE-3RD-PARTY for third-party libraries + +Introduced license details for third-party libraries used in the project. MIT license applies to 'tinycolor' and SIL Open Font License applies to 'IBMPlexSans'. This ensures proper acknowledgement and licensure compliance for used third-party resources. + +- Set up with ZDS Analysis + ## [0.0.1+12] - 2023-09-06 + ### :wrench: Chores -- [`6a2834e`](https://github.com/zebratechnologies/zeta-flutter/commit/6a2834e762c238d3927d83a239490250b1687b64) - Tidy, reorganise and prepare repo *(commit by [@thelukewalton](https://github.com/thelukewalton))* + +- [`6a2834e`](https://github.com/zebratechnologies/zeta-flutter/commit/6a2834e762c238d3927d83a239490250b1687b64) - Tidy, reorganise and prepare repo _(commit by [@thelukewalton](https://github.com/thelukewalton))_ ### :flying_saucer: Other Changes + - [`f91e8ef`](https://github.com/zebratechnologies/zeta-flutter/commit/f91e8ef85c0a1670227d66bd441513bc33e6242c) - Feature/color ([#21](https://github.com/zebratechnologies/zeta-flutter/pull/21)) * feat(color): Adding color defs -* feat(color): starting colorswatch util +* feat(color): starting colorswatch util * bug(quality): updating lint rules @@ -15,8 +89,7 @@ * bug(platforms): adding windows into example -* bug(type): Fixing reset height and tests failing *(commit by [@thelukewalton](https://github.com/thelukewalton))* - +* bug(type): Fixing reset height and tests failing _(commit by [@thelukewalton](https://github.com/thelukewalton))_ ## 0.0.1+11 - 2023-08-09 @@ -103,4 +176,4 @@ - Initial setup -[0.0.1+12]: https://github.com/zebratechnologies/zeta-flutter/compare/0.0.1+11...0.0.1+12 \ No newline at end of file +[0.0.1+12]: https://github.com/zebratechnologies/zeta-flutter/compare/0.0.1+11...0.0.1+12 From c7cffcfbc127bc63c20338ad81e02ce97b700269 Mon Sep 17 00:00:00 2001 From: Prashant Sawant <75819349+ps9310@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:03:37 -0500 Subject: [PATCH 5/6] Refactor theme declaration and introduce theme service (#8) In theme_color_switch.dart, restructured the declaration of themes into a global constant 'appThemes', freeing it from a class field in ZetaThemeColorSwitch. This increases application efficiency and adheres to the DRY (Don't Repeat Yourself) principle. A new file 'theme_service.dart' was also added, serving as an abstract class for loading and saving theme data. Various typos in 'colors.dart' and 'zeta.dart' were fixed to ensure correct execution. Theme data loading and saving operations in 'main.dart' were also updated to use this new service. --- CHANGELOG.md | 4 ++ example/lib/main.dart | 36 +++++++++++++- example/lib/pages/theme_color_switch.dart | 46 +++++++++--------- example/lib/theme_service.dart | 47 +++++++++++++++++++ .../Flutter/GeneratedPluginRegistrant.swift | 12 ----- example/pubspec.yaml | 3 +- lib/src/theme/color_swatch.dart | 1 + lib/src/theme/colors.dart | 8 ++-- lib/src/theme/theme_service.dart | 26 +++++++--- lib/src/zeta.dart | 25 +++++----- lib/zeta_flutter.dart | 1 + pubspec.yaml | 2 +- 12 files changed, 149 insertions(+), 62 deletions(-) create mode 100644 example/lib/theme_service.dart delete mode 100644 example/macos/Flutter/GeneratedPluginRegistrant.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d554425..9a39bb59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [0.1.1+1] - 2023-12-01 + +- feature: Refactor theme declaration and introduce theme service + ## [0.1.0+1] - 2023-11-28 - chore: Tidy, reorganise and prepare repo diff --git a/example/lib/main.dart b/example/lib/main.dart index 19c69098..86099838 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,15 +1,47 @@ import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:zeta_example/theme_service.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; import 'home.dart'; -void main() => runApp(const ZetaExample()); +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + final preferences = await SharedPreferences.getInstance(); + final themeService = SharedPrefsThemeService(preferences); + final themPreferences = await themeService.loadTheme(); + + runApp( + ZetaExample( + themeService: themeService, + initialThemeData: themPreferences.$1 ?? ZetaThemeData(), + initialThemeMode: themPreferences.$2 ?? ThemeMode.system, + initialContrast: themPreferences.$3 ?? ZetaContrast.aa, + ), + ); +} class ZetaExample extends StatelessWidget { - const ZetaExample({super.key}); + const ZetaExample({ + super.key, + required this.themeService, + required this.initialContrast, + required this.initialThemeMode, + required this.initialThemeData, + }); + + final ZetaThemeService themeService; + final ZetaContrast initialContrast; + final ThemeMode initialThemeMode; + final ZetaThemeData initialThemeData; @override Widget build(BuildContext context) { return ZetaProvider( + themeService: themeService, + initialContrast: initialContrast, + initialThemeData: initialThemeData, + initialThemeMode: initialThemeMode, builder: (context, themeData, themeMode) { final dark = themeData.colorsDark.toScheme(); final light = themeData.colorsLight.toScheme(); diff --git a/example/lib/pages/theme_color_switch.dart b/example/lib/pages/theme_color_switch.dart index a2ca12fc..98ebbeec 100644 --- a/example/lib/pages/theme_color_switch.dart +++ b/example/lib/pages/theme_color_switch.dart @@ -1,29 +1,29 @@ import 'package:flutter/material.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; +late final appThemes = { + "default": ZetaThemeData(), + "teal": ZetaThemeData( + identifier: 'teal', + primary: ZetaColorBase.teal, + ), + "yellow": ZetaThemeData( + identifier: 'yellow', + primary: ZetaColorBase.yellow, + ), + "red": ZetaThemeData( + identifier: 'red', + primary: ZetaColorBase.red, + ), + "purple": ZetaThemeData( + identifier: 'purple', + primary: ZetaColorBase.purple, + ), +}; + class ZetaThemeColorSwitch extends StatelessWidget { ZetaThemeColorSwitch({super.key}); - late final _themes = { - "default": ZetaThemeData(), - "teal": ZetaThemeData( - identifier: 'teal', - primary: ZetaColorBase.teal, - ), - "yellow": ZetaThemeData( - identifier: 'yellow', - primary: ZetaColorBase.yellow, - ), - "red": ZetaThemeData( - identifier: 'red', - primary: ZetaColorBase.red, - ), - "purple": ZetaThemeData( - identifier: 'purple', - primary: ZetaColorBase.purple, - ), - }; - @override Widget build(BuildContext context) { var zeta = Zeta.of(context); @@ -44,8 +44,8 @@ class ZetaThemeColorSwitch extends StatelessWidget { alignment: Alignment.center, icon: SizedBox(width: 8), dropdownColor: zeta.colors.borderDisabled, - items: _themes.entries.map((e) { - var zetaColors = primary(_themes[e.key]!); + items: appThemes.entries.map((e) { + var zetaColors = primary(appThemes[e.key]!); var color = zetaColors.primary; return DropdownMenuItem( value: e.value.identifier, @@ -58,7 +58,7 @@ class ZetaThemeColorSwitch extends StatelessWidget { ); }).toList(), onChanged: (value) { - final theme = _themes[value]; + final theme = appThemes[value]; if (theme != null) { ZetaProvider.of(context).updateThemeData(theme); } diff --git a/example/lib/theme_service.dart b/example/lib/theme_service.dart new file mode 100644 index 00000000..b25c0a45 --- /dev/null +++ b/example/lib/theme_service.dart @@ -0,0 +1,47 @@ +import 'package:flutter/src/material/app.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:zeta_example/pages/theme_color_switch.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class SharedPrefsThemeService extends ZetaThemeService { + SharedPrefsThemeService(this.preferences); + + final SharedPreferences preferences; + + @override + Future<(ZetaThemeData?, ThemeMode?, ZetaContrast?)> loadTheme() async { + final theme = preferences.getString('theme') ?? 'default'; + final themeData = appThemes[theme]; + + final modeString = preferences.getString('themeMode'); + final themeMode = modeString == 'light' + ? ThemeMode.light + : modeString == 'dark' + ? ThemeMode.dark + : ThemeMode.system; + + final contrastString = preferences.getString('contrast'); + final contrast = contrastString == 'aaa' ? ZetaContrast.aaa : ZetaContrast.aa; + + return (themeData, themeMode, contrast); + } + + @override + Future saveTheme({ + required ZetaThemeData themeData, + required ThemeMode themeMode, + required ZetaContrast contrast, + }) async { + await preferences.setString('theme', themeData.identifier); + + final modeString = themeMode == ThemeMode.light + ? 'light' + : themeMode == ThemeMode.dark + ? 'dark' + : 'system'; + await preferences.setString('themeMode', modeString); + + final contrastString = contrast == ZetaContrast.aaa ? 'aaa' : 'aa'; + await preferences.setString('contrast', contrastString); + } +} diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index e777c67d..00000000 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import path_provider_foundation - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) -} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index f6c712a9..74d5c998 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -4,13 +4,14 @@ version: 0.0.1 publish_to: "none" environment: - sdk: ">=2.19.1 <3.0.0" + sdk: ">=3.0.1 <4.0.0" dependencies: flutter: sdk: flutter go_router: ^11.1.2 google_fonts: ^6.1.0 + shared_preferences: ^2.2.2 zeta_flutter: path: ../ diff --git a/lib/src/theme/color_swatch.dart b/lib/src/theme/color_swatch.dart index 5b8f8acd..405a0440 100644 --- a/lib/src/theme/color_swatch.dart +++ b/lib/src/theme/color_swatch.dart @@ -27,6 +27,7 @@ class ZetaColorSwatch extends ColorSwatch { /// /// It ensures that the 60th and 80th shades from swatch are abide by the AA and AAA accessibility standards on [background], respectively. /// [background] color defaults to [ZetaColorBase.greyWarm] shade10. + /// {@endtemplate} factory ZetaColorSwatch.fromColor( Color primary, { Brightness brightness = Brightness.light, diff --git a/lib/src/theme/colors.dart b/lib/src/theme/colors.dart index c54f270f..9abdc4f3 100644 --- a/lib/src/theme/colors.dart +++ b/lib/src/theme/colors.dart @@ -218,13 +218,13 @@ class ZetaColors { // Text / icons. + /// {@template zeta-color-dark} /// Default text /icon color. /// /// Defaults to `ZetaColors.cool.90`. /// - /// {@template zeta-color-dark} /// Color swatches are inverted if [ZetaColors.brightness] is Dark. - /// {@endTemplate} + /// {@endtemplate} Color get textDefault => cool.shade90; /// Subtle text /icon color. @@ -252,13 +252,13 @@ class ZetaColors { /// {@macro zeta-color-dark} Color get textInverse => cool.shade20; + /// {@template zeta-color-dark} /// Default icon color. /// /// Defaults to `ZetaColors.cool.90`. /// - /// {@template zeta-color-dark} /// Color swatches are inverted if [ZetaColors.brightness] is Dark. - /// {@endTemplate} + /// {@endtemplate} Color get iconDefault => textDefault; /// Subtle icon color. diff --git a/lib/src/theme/theme_service.dart b/lib/src/theme/theme_service.dart index bbc63ee5..84b243ad 100644 --- a/lib/src/theme/theme_service.dart +++ b/lib/src/theme/theme_service.dart @@ -1,17 +1,25 @@ +import 'package:flutter/material.dart'; + +import 'contrast.dart'; import 'theme_data.dart'; /// `ZetaThemeService` is an abstract class. /// It provides the structure for loading and saving themes in Zeta application. abstract class ZetaThemeService { - /// Loads the application's theme. + /// Asynchronously load the theme data. /// - /// `loadTheme` is a method to retrieve the current theme data. + /// This method returns a `Future` that when complete will produce a + /// tuple of `ZetaThemeData`, `ThemeMode`, and `ZetaContrast`. /// - /// Returns a `Future` that completes with the `ZetaThemeData` object - /// that represents the current theme. - - Future loadTheme(); + /// `ZetaThemeData` describes the colors that are used by a theme. + /// + /// `ThemeMode` determines the brightness of the system. + /// + /// `ZetaContrast` defines different contrast styles to use across the application. + /// + /// Returns a Future `(ZetaThemeData?, ThemeMode?, ZetaContrast?)`. + Future<(ZetaThemeData?, ThemeMode?, ZetaContrast?)> loadTheme(); /// Saves the provided theme data as the application's theme. /// @@ -21,5 +29,9 @@ abstract class ZetaThemeService { /// /// Returns a `Future` that completes when the theme data has been successfully saved. - Future saveTheme(ZetaThemeData themeData); + Future saveTheme({ + required ZetaThemeData themeData, + required ThemeMode themeMode, + required ZetaContrast contrast, + }); } diff --git a/lib/src/zeta.dart b/lib/src/zeta.dart index cf8a7c42..e28de7e3 100644 --- a/lib/src/zeta.dart +++ b/lib/src/zeta.dart @@ -248,17 +248,6 @@ class ZetaProviderState extends State with Diagnosticable, Widgets // Apply the initial contrast to the theme data. _themeData = widget.initialThemeData.apply(contrast: _contrast); - - // Load theme data from themeService if available. - unawaited( - widget.themeService?.loadTheme().then((value) { - if (value != null) { - setState(() { - _themeData = value; - }); - } - }), - ); } /// Clean up function to be called when this object is removed from the tree. @@ -332,6 +321,7 @@ class ZetaProviderState extends State with Diagnosticable, Widgets void updateThemeMode(ThemeMode themeMode) { setState(() { _themeMode = themeMode; + _saveThemeChange(); }); } @@ -339,7 +329,7 @@ class ZetaProviderState extends State with Diagnosticable, Widgets void updateThemeData(ZetaThemeData data) { setState(() { _themeData = data.apply(contrast: _contrast); - unawaited(widget.themeService?.saveTheme(data)); + _saveThemeChange(); }); } @@ -348,9 +338,20 @@ class ZetaProviderState extends State with Diagnosticable, Widgets setState(() { _contrast = contrast; _themeData = _themeData.apply(contrast: contrast); + _saveThemeChange(); }); } + void _saveThemeChange() { + unawaited( + widget.themeService?.saveTheme( + themeData: _themeData, + themeMode: _themeMode, + contrast: _contrast, + ), + ); + } + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index eecb1b7a..576d2887 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -10,6 +10,7 @@ export 'src/theme/color_swatch.dart'; export 'src/theme/constants.dart'; export 'src/theme/contrast.dart'; export 'src/theme/theme_data.dart'; +export 'src/theme/theme_service.dart'; export 'src/tokens.dart'; export 'src/utils/extensions.dart'; export 'src/zeta.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 564d0e43..4a8e8fba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: zeta_flutter -version: 0.1.0+1 +version: 0.1.1+1 description: Zeta is the new, formal, standardized Zebra Design System based off the successes of ZDS (Zebra Design System). This package is in pre-release, and so many aspects are incomplete. homepage: https://github.com/zebradevs/zeta_flutter repository: https://github.com/zebradevs/zeta_flutter From 86eba6ff02346ecf9bbd2f3594b2ad61c45c85b5 Mon Sep 17 00:00:00 2001 From: Genoveva Georgieva <151932404+genovevageorgieva@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:01:25 +0200 Subject: [PATCH 6/6] Checkbox (#9) * checkbox * remove comment * widgetbook * widgetbook --- example/lib/home.dart | 3 + example/lib/pages/checkbox_example.dart | 79 +++++++ example/test/checkbox_test.dart | 73 +++++++ .../components/checkbox_widgetbook.dart | 38 ++++ example/widgetbook/widgetbook.dart | 2 + lib/src/components/checkbox.dart | 201 ++++++++++++++++++ lib/zeta_flutter.dart | 1 + 7 files changed, 397 insertions(+) create mode 100644 example/lib/pages/checkbox_example.dart create mode 100644 example/test/checkbox_test.dart create mode 100644 example/widgetbook/components/checkbox_widgetbook.dart create mode 100644 lib/src/components/checkbox.dart diff --git a/example/lib/home.dart b/example/lib/home.dart index ad1381d3..d455a251 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:zeta_example/pages/color_example.dart'; +import 'package:zeta_example/pages/checkbox_example.dart'; import 'package:zeta_example/pages/grid_example.dart'; import 'package:zeta_example/pages/spacing_example.dart'; import 'package:zeta_example/pages/typography_example.dart'; @@ -19,6 +20,8 @@ final List components = [ Component(SpacingExample.name, (context) => const SpacingExample()), Component(TypographyExample.name, (context) => const TypographyExample()), Component(ColorExample.name, (context) => const ColorExample()), + Component(CheckBoxExample.name, (context) => const CheckBoxExample()), + ]; class Home extends StatefulWidget { diff --git a/example/lib/pages/checkbox_example.dart b/example/lib/pages/checkbox_example.dart new file mode 100644 index 00000000..37ab826d --- /dev/null +++ b/example/lib/pages/checkbox_example.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../widgets.dart'; + +class CheckBoxExample extends StatefulWidget { + static const String name = 'Checkbox'; + + const CheckBoxExample({Key? key}) : super(key: key); + + @override + State createState() => _CheckBoxExampleState(); +} + +class _CheckBoxExampleState extends State { + bool? isChecked = true; + bool isEnabled = true; + + @override + Widget build(BuildContext context) { + return ExampleScaffold( + name: 'Checkbox', + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ZetaCheckbox( + value: isChecked, + isEnabled: isEnabled, + onChanged: (value) => setState(() => isChecked = value)), + ElevatedButton( + child: const Text('Disable'), + onPressed: () => setState(() => isEnabled = !isEnabled), + ) + ], + ), + Row(children: [const Text('Sharp Checkbox Enabled')]), + getCheckBoxRow(isEnabled: true), + Row(children: [const Text('Sharp Checkbox Disabled')]), + getCheckBoxRow(isEnabled: false), + Row(children: [const Text('Rounded Checkbox Enabled')]), + getCheckBoxRow(isEnabled: true, isSharp: false), + Row(children: [const Text('Rounded Checkbox Disabled')]), + getCheckBoxRow(isEnabled: false, isSharp: false), + ], + ), + ), + ); + } +} + +Row getCheckBoxRow({required bool isEnabled, bool isSharp = true}) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ZetaCheckbox( + value: true, + isEnabled: isEnabled, + label: 'Selected', + borderType: isSharp ? BorderType.sharp : BorderType.rounded, + onChanged: (value) => {}), + ZetaCheckbox( + value: false, + isEnabled: isEnabled, + label: 'Indeterminate', + borderType: isSharp ? BorderType.sharp : BorderType.rounded, + onChanged: (value) => {}), + ZetaCheckbox( + value: null, + borderType: isSharp ? BorderType.sharp : BorderType.rounded, + isEnabled: isEnabled, + onChanged: (value) => {}), + ]); +} diff --git a/example/test/checkbox_test.dart b/example/test/checkbox_test.dart new file mode 100644 index 00000000..abc531a6 --- /dev/null +++ b/example/test/checkbox_test.dart @@ -0,0 +1,73 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; +import 'package:flutter/material.dart'; + +void main() { + group('ZetaCheckbox Tests', () { + testWidgets('Initializes with correct parameters', + (WidgetTester tester) async { + await tester.pumpWidget( + TestWidgetCB( + widget: ZetaCheckbox( + value: true, + onChanged: (value) {}, + borderType: BorderType.rounded, + label: 'Test Checkbox', + checkboxSize: Size(30, 30), + )), + ); + + final checkboxFinder = find.byType(ZetaCheckbox); + final ZetaCheckbox checkbox = tester.firstWidget(checkboxFinder); + + expect(checkbox.value, true); + expect(checkbox.borderType, BorderType.rounded); + expect(checkbox.label, 'Test Checkbox'); + expect(checkbox.checkboxSize, Size(30, 30)); + }); + + testWidgets('ZetaCheckbox changes state on tap', (WidgetTester tester) async { + bool? checkboxValue = true; + + await tester.pumpWidget( + TestWidgetCB( + widget: ZetaCheckbox( + value: checkboxValue, + onChanged: (value) { + checkboxValue = value; + }, + )), + ); + + await tester.tap(find.byType(ZetaCheckbox)); + await tester.pump(); + + expect(checkboxValue, false); + }); + }); +} + +class TestWidgetCB extends StatelessWidget { + final Widget widget; + + const TestWidgetCB({Key? key, required this.widget}); + + @override + Widget build(BuildContext context) { + return ZetaProvider( + builder: (context, theme, __) { + return Builder(builder: (context) { + return MaterialApp( + theme: ThemeData( + fontFamily: theme.fontFamily, + textTheme: ZetaText.textTheme, + ), + home: Scaffold( + body: widget, + ), + ); + }); + }, + ); + } +} diff --git a/example/widgetbook/components/checkbox_widgetbook.dart b/example/widgetbook/components/checkbox_widgetbook.dart new file mode 100644 index 00000000..6909ee4e --- /dev/null +++ b/example/widgetbook/components/checkbox_widgetbook.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:zeta_example/pages/checkbox_example.dart'; + +WidgetbookComponent checkboxWidgetBook() { + return WidgetbookComponent( + name: 'Checkbox', + useCases: [ + WidgetbookUseCase( + name: 'Checkbox (sharp)', + builder: (context) => SingleChildScrollView( + child: Padding( + padding: EdgeInsets.only(top: 10), + child: getCheckBoxRow(isEnabled: true), + ), + ), + ), + WidgetbookUseCase( + name: 'Checkbox (rounded)', + builder: (context) => SingleChildScrollView( + child: Padding( + padding: EdgeInsets.only(top: 10), + child: getCheckBoxRow(isEnabled: true, isSharp: false), + ), + ), + ), + WidgetbookUseCase( + name: 'Checkbox disabled (rounded)', + builder: (context) => SingleChildScrollView( + child: Padding( + padding: EdgeInsets.only(top: 10), + child: getCheckBoxRow(isEnabled: false, isSharp: false), + ), + ), + ), + ], + ); +} diff --git a/example/widgetbook/widgetbook.dart b/example/widgetbook/widgetbook.dart index e1c128e3..df609965 100644 --- a/example/widgetbook/widgetbook.dart +++ b/example/widgetbook/widgetbook.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:widgetbook/widgetbook.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; +import 'components/checkbox_widgetbook.dart'; import 'components/color_widgetbook.dart'; import 'components/grid_widgetbook.dart'; import 'components/spacing_widgetbook.dart'; @@ -24,6 +25,7 @@ class HotReload extends StatelessWidget { spacingWidgetbook(), textWidgetBook(), colorWidgetBook(), + checkboxWidgetBook() ], ), ], diff --git a/lib/src/components/checkbox.dart b/lib/src/components/checkbox.dart new file mode 100644 index 00000000..10a4c92b --- /dev/null +++ b/lib/src/components/checkbox.dart @@ -0,0 +1,201 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import '../../zeta_flutter.dart'; + +/// Zeta Checkbox border type +enum BorderType { + ///sharp border + sharp, + + ///rounded border + rounded, +} + +///Zeta Checkbox +class ZetaCheckbox extends StatelessWidget { + /// Constructs a [ZetaCheckbox]. + const ZetaCheckbox({ + required this.value, + required this.onChanged, + this.borderType = BorderType.sharp, + this.label, + this.labelStyle, + this.checkboxSize = const Size(25, 25), + this.selectedColor, + this.unselectedColor, + this.unselectedBorderColor, + this.unselectedBorderWidth = 2.5, + this.disabledColor, + this.isEnabled = true, + this.iconSize = 18.0, + super.key, + }) : assert(iconSize > 0, 'Icon size must be greater than 0'); + + /// Whether the checkbox is selected, unselected or null (indeterminate) + final bool? value; + + /// Called when the value of the checkbox should change. + final ValueChanged onChanged; + + /// The type of border to display + /// + /// defaults to sharp + final BorderType borderType; + + /// The label displayed next to the checkbox + final String? label; + + /// Style to use on the label + final TextStyle? labelStyle; + + ///Size of the checkbox + final Size checkboxSize; + + /// The color to use when this checkbox is checked. + final Color? selectedColor; + + ///Color of the checkbox when it's not selected + final Color? unselectedColor; + + ///Color of the border when the checkbox not selected + final Color? unselectedBorderColor; + + ///Width of the border when the checkbox is not selected + /// + /// Defaults to 2.5 + final double unselectedBorderWidth; + + ///Size of the icon displayed inside the checkbox + final double iconSize; + + ///If checkbox is enabled + /// + ///defaults to true + final bool isEnabled; + + ///Color of the checkbox when it's disabled + final Color? disabledColor; + + @override + Widget build(BuildContext context) { + final theme = Zeta.of(context); + final ValueNotifier isFocused = ValueNotifier(false); + + return FocusableActionDetector( + onFocusChange: (bool focus) => isFocused.value = focus, + child: GestureDetector( + onTap: _handleOnTap, + child: _buildCheckboxRow(theme, isFocused), + ), + ); + } + + void _handleOnTap() { + if (isEnabled) { + onChanged( + value == null + ? true + : value! + ? false + : null, + ); + } + } + + Widget _buildCheckboxRow( + Zeta theme, + ValueNotifier isFocused, + ) { + return Flex( + direction: Axis.horizontal, + mainAxisSize: MainAxisSize.min, + children: [ + _buildCheckboxContainer(theme, isFocused), + if (label != null) ...[ + Flexible( + child: Padding( + padding: const EdgeInsets.only(left: Dimensions.s), + child: Text( + label!, + style: labelStyle ?? ZetaText.zetaTitleMedium, + ), + ), + ), + ], + ], + ); + } + + Widget _buildCheckboxContainer(Zeta theme, ValueNotifier isFocused) { + return ValueListenableBuilder( + valueListenable: isFocused, + builder: (context, focused, child) { + return Container( + decoration: BoxDecoration( + boxShadow: _getBoxShadow(theme, focused), + color: _getCheckboxBackgroundColor(theme), + border: Border.all( + color: _getCheckboxBorderColor(theme), + width: unselectedBorderWidth, + ), + borderRadius: BorderRadius.circular( + borderType == BorderType.rounded ? 4.0 : 0.0, + ), + ), + width: checkboxSize.width, + height: checkboxSize.height, + child: _getCheckboxIcon(theme), + ); + }, + ); + } + + List _getBoxShadow(Zeta theme, bool focused) { + if (!focused) return []; + return [ + BoxShadow( + spreadRadius: 3, + color: theme.colors.surfaceSelectedHovered, + ), + ]; + } + + Widget _getCheckboxIcon(Zeta theme) { + if (value == null) return const SizedBox.shrink(); + return Icon( + value! ? Icons.check : Icons.remove, + color: isEnabled ? theme.colors.white : theme.colors.textDisabled, + size: iconSize, + ); + } + + Color _getCheckboxBackgroundColor(Zeta theme) { + if (!isEnabled) return disabledColor ?? theme.colors.surfaceDisabled; + if (value == null) return unselectedColor ?? theme.colors.surfaceSecondary; + return selectedColor ?? theme.colors.primary; + } + + Color _getCheckboxBorderColor(Zeta theme) { + if (!isEnabled || value != null) return Colors.transparent; + return unselectedBorderColor ?? theme.colors.borderDefault; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('value', value)) + ..add(ObjectFlagProperty>.has('onChanged', onChanged)) + ..add(EnumProperty('borderType', borderType)) + ..add(StringProperty('label', label)) + ..add(DiagnosticsProperty('labelStyle', labelStyle)) + ..add(DiagnosticsProperty('checkboxSize', checkboxSize)) + ..add(ColorProperty('selectedColor', selectedColor)) + ..add(ColorProperty('unselectedColor', unselectedColor)) + ..add(ColorProperty('unselectedBorderColor', unselectedBorderColor)) + ..add(DoubleProperty('unselectedBorderWidth', unselectedBorderWidth)) + ..add(ColorProperty('disabledColor', disabledColor)) + ..add(DiagnosticsProperty('isEnabled', isEnabled)) + ..add(DoubleProperty('iconSize', iconSize)); + } +} diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index 576d2887..f4babe3b 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -1,6 +1,7 @@ /// Zebra Design System (Zeta) - Flutter Component Library library zeta_flutter; +export 'src/components/checkbox.dart'; export 'src/components/grid.dart'; export 'src/components/spacing.dart'; export 'src/components/text.dart';