diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b01303..f6785c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,13 +4,16 @@ on: pull_request: push: branches: [master] + schedule: + - cron: '0 6 * * 1' # Mondays 06:00 UTC: catch breakage from new Flutter releases + workflow_dispatch: jobs: test: name: Test & Analyze runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: subosito/flutter-action@v2 with: @@ -27,3 +30,24 @@ jobs: - name: Test run: flutter test --coverage --test-randomize-ordering-seed random + + test-beta: + # Early warning for upcoming deprecations and breaking changes. Allowed to fail. + name: Test & Analyze (beta) + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v5 + + - uses: subosito/flutter-action@v2 + with: + channel: beta + + - name: Install dependencies + run: flutter pub get + + - name: Analyze + run: flutter analyze lib/ test/ example/ + + - name: Test + run: flutter test --test-randomize-ordering-seed random diff --git a/.github/workflows/code-quality-tests.yml b/.github/workflows/code-quality-tests.yml deleted file mode 100644 index ca938a7..0000000 --- a/.github/workflows/code-quality-tests.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Code Quality - -on: [push] - -jobs: - format-and-fix: - if: ${{ !github.event.pull_request.draft }} - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup flutter - uses: subosito/flutter-action@v2 - with: - channel: stable - - - name: Get packages - run: flutter pub get - - - name: Check format errors - run: flutter format --set-exit-if-changed . - - - name: Check lint errors - run: flutter analyze . - - - name: Run tests - run: flutter test --coverage --test-randomize-ordering-seed random - - - name: Very Good Coverage - uses: VeryGoodOpenSource/very_good_coverage@v2.1.0 - with: - min_coverage: 35 diff --git a/.github/workflows/conventional-pr-title.yml b/.github/workflows/conventional-pr-title.yml index dfb7f4f..aa0f51b 100644 --- a/.github/workflows/conventional-pr-title.yml +++ b/.github/workflows/conventional-pr-title.yml @@ -12,11 +12,9 @@ jobs: main: name: Validate PR title runs-on: ubuntu-latest + permissions: + pull-requests: read steps: - - uses: deepakputhraya/action-pr-title@master - with: - max_length: 100 - github_token: ${{ secrets.GITHUB_TOKEN }} - - uses: amannn/action-semantic-pull-request@v4 + - uses: amannn/action-semantic-pull-request@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 27029f1..13c372a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,19 +1,21 @@ +# Automated publishing: https://dart.dev/tools/pub/automated-publishing +# Needs, on pub.dev (Admin tab): automated publishing enabled for this repository, +# tag pattern `v{{version}}` and required environment `pub.dev`. On GitHub: an +# environment named `pub.dev`. Release by pushing a tag that matches the pubspec +# version, e.g. `git tag v3.3.0 && git push origin v3.3.0`. name: Publish to pub.dev on: push: tags: - - 'v*' + - 'v[0-9]+.[0-9]+.[0-9]+' # tag pattern on pub.dev: 'v{{version}}' jobs: - publish: - name: Publish + test: + name: Test & Analyze runs-on: ubuntu-latest - permissions: - id-token: write # required for OIDC pub.dev publishing - steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: subosito/flutter-action@v2 with: @@ -28,5 +30,29 @@ jobs: - name: Test run: flutter test --test-randomize-ordering-seed random + publish: + # Separate job so the short-lived OIDC token is created right before publishing. + name: Publish + needs: test + runs-on: ubuntu-latest + environment: pub.dev + permissions: + contents: read + id-token: write # Required for authentication using OIDC + steps: + - uses: actions/checkout@v5 + + # Creates the GitHub-signed OIDC token and registers it with pub for pub.dev. + - uses: dart-lang/setup-dart@v1 + + # Flutter's `dart` shadows the one from setup-dart; publishing a Flutter + # package needs the Flutter SDK. + - uses: subosito/flutter-action@v2 + with: + channel: stable + + - name: Install dependencies + run: flutter pub get + - name: Publish run: flutter pub publish --force diff --git a/.gitignore b/.gitignore index c2853e0..a7e6824 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ version **/doc/api/ .dart_tool/ .flutter-plugins +.flutter-plugins-dependencies .packages .pub-cache/ .pub/ @@ -47,6 +48,7 @@ unlinked_spec.ds # Android related #**/android/**/gradle-wrapper.jar **/android/.gradle +**/android/.kotlin/ **/android/captures/ #**/android/gradlew #**/android/gradlew.bat @@ -85,3 +87,14 @@ unlinked_spec.ds !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages + +# Generated / local-only files +coverage/ +migrate_working_dir/ +.build/ +.swiftpm/ +**/ios/Flutter/ephemeral/ +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/flutter_export_environment.sh +**/ios/Flutter/.last_build_id +**/windows/flutter/ephemeral/ diff --git a/.pubignore b/.pubignore new file mode 100644 index 0000000..52a1f48 --- /dev/null +++ b/.pubignore @@ -0,0 +1,117 @@ +# pub ignores .gitignore in a directory that has a .pubignore, so the rules below +# start as a copy of .gitignore. Keep them in sync. + +# Miscellaneous +*.class +*.lock +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# Visual Studio Code related +.vscode/ + +# Flutter repo-specific +/bin/cache/ +/bin/mingit/ +/dev/benchmarks/mega_gallery/ +/dev/bots/.recipe_deps +/dev/bots/android_tools/ +/dev/docs/doc/ +/dev/docs/lib/ +/dev/docs/pubspec.yaml +/packages/flutter/coverage/ +version + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ +flutter_*.png +linked_*.ds +unlinked.ds +unlinked_spec.ds + +# Android related +#**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/.kotlin/ +**/android/captures/ +#**/android/gradlew +#**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/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/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages + +# Generated / local-only files +coverage/ +migrate_working_dir/ +.build/ +.swiftpm/ +**/ios/Flutter/ephemeral/ +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/flutter_export_environment.sh +**/ios/Flutter/.last_build_id +**/windows/flutter/ephemeral/ + +# Not part of the published package +AGENTS.md +CLAUDE.md +PLAN.md +/coverage/ +/example/coverage/ +/example/ios/Flutter/ephemeral/ +.history/ +.idea/ + +# README loads these from GitHub URLs. Only the pubspec `screenshots:` image ships. +/images/* +!/images/pub_dev_preview.png diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d524238 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,67 @@ +# AGENTS.md + +Guidance for coding agents (Claude Code, Codex, ...) working in this repository. + +## Commands + +```bash +# Get dependencies +flutter pub get + +# Run all tests (with randomized ordering) +flutter test --coverage --test-randomize-ordering-seed random + +# Run a single test file +flutter test test/badges_test.dart + +# Lint (CI runs exactly this; infos fail it) +flutter analyze lib/ test/ example/ + +# Format (CI enforces this — run before committing) +dart format . +``` + +## Architecture + +This is a Flutter package (`badges`) that provides a single `Badge` widget for overlaying notification-style badges on any widget. + +### Public API (`lib/badges.dart`) + +All public exports go through `lib/badges.dart`. Consumers import as: +```dart +import 'package:badges/badges.dart' as badges; +``` +The `as badges` alias is required because Flutter 3.7+ added a `Badge` widget to Material that conflicts. + +### Core widget (`lib/src/badge.dart`) + +`Badge` is a `StatefulWidget` with `TickerProviderStateMixin`. It manages two `AnimationController`s: +- `_animationController` — drives the content-change animation (slide, scale, fade, size, rotation) +- `_appearanceController` — drives the fade in/out when `showBadge` toggles + +The `didUpdateWidget` override is where animation re-triggering logic lives: it watches for changes to `badgeContent` (Text/Icon data), `badgeColor`, `showBadge`, and `loopAnimation`. + +When `child` is null, the badge renders standalone. When `child` is provided, it uses a `Stack` with `BadgePositioned` to overlay the badge. When `onTap` is set, extra padding is added to the child and the position is recalculated via `CalculationUtils` to keep the full badge tappable. + +### Configuration objects + +- **`BadgeStyle`** — visual properties: shape, color, gradient, border, padding, elevation +- **`BadgeAnimation`** — named constructors per animation type (`.slide()`, `.fade()`, `.scale()`, `.size()`, `.rotation()`); each sets the `animationType` field and relevant defaults +- **`BadgePosition`** — named constructors (`topEnd`, `topStart`, `bottomEnd`, `bottomStart`, `center`) plus custom offsets +- **`BadgeGradient`** — named constructors (`.linear()`, `.radial()`, `.sweep()`) wrapping Flutter gradient types +- **`BadgeShape`** — enum: `circle`, `square`, `twitter`, `instagram` + +### Custom shapes + +`BadgeShape.twitter` and `BadgeShape.instagram` bypass the `Material`/`AnimatedContainer` path and use `CustomPaint` with their respective painters in `lib/src/painters/`. The `DrawingUtils.drawBadgeShape()` helper selects the correct painter. + +### Internal utilities (not exported) + +- `lib/src/utils/calculation_utils.dart` — computes padding and position adjustments for tappable badges +- `lib/src/utils/gradient_utils.dart` — gradient rendering helpers +- `lib/src/badge_border_gradient.dart` — custom `BoxBorder` subclass that paints a gradient border +- `lib/src/badge_gradient_type.dart` — internal enum used by `BadgeGradient` + +### Tests (`test/`) + +`badges_test.dart` is the entry point; it imports and calls group functions from `test/badge_animations_tests/`. Tests use `flutter_test` and wrap widgets in `MaterialApp` + `Scaffold`. Animation tests pump specific durations and check `hasRunningAnimations` to assert controller state. diff --git a/CHANGELOG.md b/CHANGELOG.md index 77b9a59..1e35e42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,29 @@ -## [4.0.0] - [September 23, 2026] +## [4.0.0] - [Unreleased] ### Breaking Changes -* **Migrated to `package:material_ui`** — Material and Cupertino were decoupled from the Flutter SDK in Flutter 3.47 and now ship as standalone packages. This package now imports `package:material_ui/material_ui.dart` instead of `package:flutter/material.dart`. +* **Migrated to `package:material_ui`** (#135, thanks @mayainle) — Material and Cupertino were decoupled from the Flutter SDK in Flutter 3.47 and now ship as standalone packages. This package now imports `package:material_ui/material_ui.dart` instead of `package:flutter/material.dart`. * In apps that use `material_ui`, badge content now gets the app's theme text style without `MaterialUiCompatibilityBridge`. Apps still on `package:flutter/material.dart` keep working, but badge text uses the default Material text style until they migrate. This is a major version bump, as the Flutter team recommends for this migration. * To hide the ambiguous `Badge`, use `import 'package:material_ui/material_ui.dart' hide Badge;` instead of the Flutter Material equivalent. * **Minimum SDK raised** to Dart `3.13.0` / Flutter `3.47.0`, the floor required by `material_ui`. +### Bug Fixes +* **Issue #133** — The `ignorePointer` docs said the opposite of what it does. `true` lets taps pass through the badge; `false` (default) lets the badge get taps. +* **`BadgeStyle.elevation` works again.** It has drawn no shadow since 3.0.3. The default is now `0`, so badges that don't set it look the same as before. +* **`BadgePosition.center()` with `onTap`** no longer moves the badge to the top-left corner. +* **`BadgePosition.centerStart()` / `centerEnd()`** are now vertically centered, as documented. +* **Right-to-left layouts with `onTap`** no longer shift the badge. The tap padding is now directional. +* **Hidden badges (`showBadge: false`)** no longer catch taps or call `onTap`; taps reach the widget below. +* **Twitter / Instagram shapes** no longer crash with gradients of 3+ colors. Gradient `stops`, `tileMode`, sweep angles and directional alignments are now used. +* **Turning `toAnimate` on at runtime** no longer leaves the badge invisible. +* Fixed a listener leak: every rebuild added a listener to the appearance controller. +* Removed a no-op `ConstrainedBox`. The 3.2.0 "minimum-square sizing" note was wrong: badge sizing did not change in 3.2.0 and does not change now. + ### Maintenance * Replaced `SizeTransition.axisAlignment`, deprecated after Flutter v3.41, with `alignment`. `BadgeAnimation.sizeTransitionAxisAlignment` is unchanged and still takes a `double`. +* CI: removed the broken "Code Quality" workflow, set up pub.dev automated publishing in the publish workflow, updated actions, added a weekly run on Flutter beta. +* Example app: regenerated Android / iOS / Windows projects so it builds again, and fixed the test screen overflowing on phone widths. +* Smaller pub.dev archive (`.pubignore`). +* README: fixed build and downloads badges, documented `onTap` padding and `ignorePointer`. ## [3.2.0] - [April 9, 2026] diff --git a/CLAUDE.md b/CLAUDE.md index 92aace5..43c994c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,67 +1 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Commands - -```bash -# Get dependencies -flutter pub get - -# Run all tests (with randomized ordering) -flutter test --coverage --test-randomize-ordering-seed random - -# Run a single test file -flutter test test/badges_test.dart - -# Lint -flutter analyze . - -# Format (CI enforces this — run before committing) -flutter format . -``` - -## Architecture - -This is a Flutter package (`badges`) that provides a single `Badge` widget for overlaying notification-style badges on any widget. - -### Public API (`lib/badges.dart`) - -All public exports go through `lib/badges.dart`. Consumers import as: -```dart -import 'package:badges/badges.dart' as badges; -``` -The `as badges` alias is required because Flutter 3.7+ added a `Badge` widget to Material that conflicts. - -### Core widget (`lib/src/badge.dart`) - -`Badge` is a `StatefulWidget` with `TickerProviderStateMixin`. It manages two `AnimationController`s: -- `_animationController` — drives the content-change animation (slide, scale, fade, size, rotation) -- `_appearanceController` — drives the fade in/out when `showBadge` toggles - -The `didUpdateWidget` override is where animation re-triggering logic lives: it watches for changes to `badgeContent` (Text/Icon data), `badgeColor`, `showBadge`, and `loopAnimation`. - -When `child` is null, the badge renders standalone. When `child` is provided, it uses a `Stack` with `BadgePositioned` to overlay the badge. When `onTap` is set, extra padding is added to the child and the position is recalculated via `CalculationUtils` to keep the full badge tappable. - -### Configuration objects - -- **`BadgeStyle`** — visual properties: shape, color, gradient, border, padding, elevation -- **`BadgeAnimation`** — named constructors per animation type (`.slide()`, `.fade()`, `.scale()`, `.size()`, `.rotation()`); each sets the `animationType` field and relevant defaults -- **`BadgePosition`** — named constructors (`topEnd`, `topStart`, `bottomEnd`, `bottomStart`, `center`) plus custom offsets -- **`BadgeGradient`** — named constructors (`.linear()`, `.radial()`, `.sweep()`) wrapping Flutter gradient types -- **`BadgeShape`** — enum: `circle`, `square`, `twitter`, `instagram` - -### Custom shapes - -`BadgeShape.twitter` and `BadgeShape.instagram` bypass the `Material`/`AnimatedContainer` path and use `CustomPaint` with their respective painters in `lib/src/painters/`. The `DrawingUtils.drawBadgeShape()` helper selects the correct painter. - -### Internal utilities (not exported) - -- `lib/src/utils/calculation_utils.dart` — computes padding and position adjustments for tappable badges -- `lib/src/utils/gradient_utils.dart` — gradient rendering helpers -- `lib/src/badge_border_gradient.dart` — custom `BoxBorder` subclass that paints a gradient border -- `lib/src/badge_gradient_type.dart` — internal enum used by `BadgeGradient` - -### Tests (`test/`) - -`badges_test.dart` is the entry point; it imports and calls group functions from `test/badge_animations_tests/`. Tests use `flutter_test` and wrap widgets in `MaterialApp` + `Scaffold`. Animation tests pump specific durations and check `hasRunningAnimations` to assert controller state. +@AGENTS.md diff --git a/README.md b/README.md index 1005b58..2175a5b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Pub Version](https://img.shields.io/pub/v/badges?color=blueviolet)](https://pub.dev/packages/badges) -[![popularity](https://img.shields.io/pub/popularity/badges?logo=dart)](https://pub.dev/packages/badges/score) +[![downloads](https://img.shields.io/pub/dm/badges?logo=dart)](https://pub.dev/packages/badges/score) [![likes](https://img.shields.io/pub/likes/badges?logo=dart)](https://pub.dev/packages/badges/score) -![building](https://github.com/yako-dev/flutter_badges/actions/workflows/code-quality-tests.yml/badge.svg) +[![CI](https://github.com/yako-dev/flutter_badges/actions/workflows/ci.yml/badge.svg)](https://github.com/yako-dev/flutter_badges/actions/workflows/ci.yml) [![style: flutter lints](https://img.shields.io/badge/style-flutter__lints-blue)](https://pub.dev/packages/flutter_lints) [![Package of the week](https://img.shields.io/badge/Package%20of-the%20week-orange)](https://youtu.be/_CIHLJHVoN8) @@ -82,6 +82,10 @@ import 'package:material_ui/material_ui.dart' hide Badge; ), ``` +`onTap`: when the badge sits outside its child (negative `position` values), the badge adds padding around the child so the whole badge can be tapped. Flutter only delivers taps inside a widget's bounds. The padding makes the widget bigger, so leave `onTap` null if you don't need it. + +`ignorePointer: true` lets taps pass through the badge to the widgets below it (and `onTap` is not called). +
--- @@ -136,6 +140,6 @@ From left to right:
[Diagonal decoration](https://pub.dev/packages/diagonal_decoration) -[Yako Yheme Switch](https://pub.dev/packages/yako_theme_switch) +[Yako Theme Switch](https://pub.dev/packages/yako_theme_switch) and more to come! \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index 57346b5..9c66398 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,3 +1,6 @@ +analyzer: + exclude: + - build/** include: package:flutter_lints/flutter.yaml linter: rules: diff --git a/coverage/lcov.info b/coverage/lcov.info deleted file mode 100644 index 963f609..0000000 --- a/coverage/lcov.info +++ /dev/null @@ -1,472 +0,0 @@ -SF:lib/src/badge_border_gradient.dart -DA:7,1 -DA:9,1 -DA:12,1 -DA:15,1 -DA:16,2 -DA:18,1 -DA:21,0 -DA:30,0 -DA:31,0 -DA:33,0 -DA:35,0 -DA:37,0 -DA:44,0 -DA:45,0 -DA:46,0 -DA:49,0 -DA:50,0 -DA:51,0 -DA:52,0 -DA:55,1 -DA:60,0 -DA:61,0 -DA:62,0 -DA:63,0 -DA:64,0 -LF:25 -LH:7 -end_of_record -SF:lib/src/painters/instagram_badge_shape_painter.dart -DA:11,1 -DA:19,0 -DA:21,0 -DA:22,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:28,0 -DA:29,0 -DA:30,0 -DA:36,0 -DA:37,0 -DA:38,0 -DA:39,0 -DA:41,0 -DA:42,0 -DA:43,0 -DA:49,0 -DA:50,0 -DA:51,0 -DA:52,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:56,0 -DA:57,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:66,0 -DA:67,0 -DA:68,0 -DA:69,0 -DA:70,0 -DA:71,0 -DA:72,0 -DA:73,0 -DA:75,0 -DA:76,0 -DA:77,0 -DA:78,0 -DA:82,1 -LF:47 -LH:2 -end_of_record -SF:lib/src/painters/twitter_badge_shape_painter.dart -DA:11,1 -DA:19,0 -DA:21,0 -DA:22,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:28,0 -DA:29,0 -DA:30,0 -DA:36,0 -DA:37,0 -DA:38,0 -DA:39,0 -DA:41,0 -DA:42,0 -DA:43,0 -DA:49,0 -DA:50,0 -DA:51,0 -DA:52,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:56,0 -DA:57,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:67,0 -DA:68,0 -DA:69,0 -DA:70,0 -DA:74,1 -LF:39 -LH:2 -end_of_record -SF:lib/src/utils/calculation_utils.dart -DA:7,1 -DA:9,1 -DA:12,1 -DA:16,1 -DA:19,1 -DA:20,2 -DA:21,2 -DA:22,2 -DA:23,2 -DA:29,1 -DA:34,1 -DA:38,1 -DA:39,1 -DA:42,1 -DA:45,2 -DA:46,1 -DA:47,2 -DA:48,2 -DA:50,1 -DA:51,2 -DA:52,2 -DA:53,2 -DA:54,2 -DA:58,1 -DA:63,1 -DA:64,3 -DA:65,1 -DA:66,3 -DA:67,1 -DA:68,3 -DA:69,1 -DA:70,3 -DA:71,1 -DA:72,3 -DA:73,1 -DA:74,3 -DA:75,1 -DA:76,3 -DA:77,1 -DA:78,3 -DA:79,1 -DA:80,3 -DA:82,1 -LF:43 -LH:43 -end_of_record -SF:lib/src/utils/drawing_utils.dart -DA:7,1 -DA:15,1 -DA:16,1 -DA:22,1 -DA:23,1 -DA:29,1 -DA:30,1 -LF:7 -LH:7 -end_of_record -SF:lib/src/badge.dart -DA:8,2 -DA:19,1 -DA:56,1 -DA:57,1 -DA:66,1 -DA:68,1 -DA:69,1 -DA:70,5 -DA:71,2 -DA:72,3 -DA:73,3 -DA:76,2 -DA:77,3 -DA:78,3 -DA:82,2 -DA:83,1 -DA:84,3 -DA:87,5 -DA:88,2 -DA:89,2 -DA:91,4 -DA:92,2 -DA:93,2 -DA:100,1 -DA:102,2 -DA:103,2 -DA:104,0 -DA:105,4 -DA:107,1 -DA:108,2 -DA:110,1 -DA:113,2 -DA:114,2 -DA:115,1 -DA:116,3 -DA:117,2 -DA:119,1 -DA:122,2 -DA:123,2 -DA:124,3 -DA:125,2 -DA:126,2 -DA:127,4 -DA:134,1 -DA:135,3 -DA:136,2 -DA:140,1 -DA:141,2 -DA:144,2 -DA:147,1 -DA:148,4 -DA:149,1 -DA:150,3 -DA:151,3 -DA:153,1 -DA:154,3 -DA:155,3 -DA:157,3 -DA:159,4 -DA:160,4 -DA:162,3 -DA:163,1 -DA:164,4 -DA:165,4 -DA:169,1 -DA:170,1 -DA:171,1 -DA:172,1 -DA:173,1 -DA:174,1 -DA:175,1 -DA:177,1 -DA:178,1 -DA:179,3 -DA:180,3 -DA:181,3 -DA:182,3 -DA:183,3 -DA:185,1 -DA:186,3 -DA:187,2 -DA:190,1 -DA:192,3 -DA:193,1 -DA:194,3 -DA:195,3 -DA:196,3 -DA:198,4 -DA:199,1 -DA:200,3 -DA:203,3 -DA:206,1 -DA:207,3 -DA:209,4 -DA:211,3 -DA:214,1 -DA:215,3 -DA:216,2 -DA:225,3 -DA:226,4 -DA:227,1 -DA:228,3 -DA:229,1 -DA:230,2 -DA:231,1 -DA:232,4 -DA:234,3 -DA:235,4 -DA:237,3 -DA:238,4 -DA:240,1 -DA:241,1 -DA:242,3 -DA:244,3 -DA:245,1 -DA:247,4 -DA:249,1 -DA:250,1 -DA:251,1 -DA:256,1 -DA:259,1 -DA:261,1 -DA:262,3 -DA:263,6 -DA:264,2 -DA:265,3 -DA:266,2 -DA:267,2 -DA:270,4 -DA:271,2 -DA:272,2 -DA:273,2 -DA:278,5 -DA:279,2 -DA:280,1 -DA:281,3 -DA:282,2 -DA:283,3 -DA:284,2 -DA:285,2 -DA:286,4 -DA:287,0 -DA:288,0 -DA:295,5 -DA:296,2 -DA:297,1 -DA:298,3 -DA:299,0 -DA:300,0 -DA:301,0 -DA:302,0 -DA:303,0 -DA:309,3 -DA:310,2 -DA:311,1 -DA:312,0 -DA:313,0 -DA:317,3 -DA:318,2 -DA:319,1 -DA:320,2 -DA:322,3 -DA:323,0 -DA:324,0 -DA:325,3 -DA:326,2 -DA:327,2 -DA:332,1 -DA:334,2 -DA:335,2 -DA:336,1 -LF:171 -LH:159 -end_of_record -SF:lib/src/badge_position.dart -DA:23,2 -DA:33,1 -DA:39,1 -DA:40,1 -DA:45,1 -DA:46,1 -DA:51,1 -DA:52,1 -DA:57,1 -DA:58,1 -DA:61,1 -DA:68,1 -LF:12 -LH:12 -end_of_record -SF:lib/src/badge_positioned.dart -DA:20,1 -DA:21,1 -DA:23,1 -DA:25,1 -DA:27,1 -DA:28,1 -DA:29,1 -DA:30,1 -DA:31,1 -DA:35,1 -DA:36,0 -DA:37,0 -DA:39,0 -DA:44,1 -DA:45,1 -DA:46,1 -DA:47,1 -DA:48,1 -DA:49,1 -LF:19 -LH:16 -end_of_record -SF:lib/src/badge_style.dart -DA:36,3 -LF:1 -LH:1 -end_of_record -SF:lib/src/badge_animation.dart -DA:70,3 -DA:88,2 -DA:103,2 -DA:118,2 -DA:133,2 -DA:153,2 -DA:155,1 -DA:156,3 -LF:8 -LH:8 -end_of_record -SF:lib/src/badge_gradient.dart -DA:20,1 -DA:35,1 -DA:50,1 -DA:65,1 -DA:66,1 -DA:67,1 -DA:68,1 -DA:69,1 -DA:70,1 -DA:71,1 -DA:72,1 -DA:73,1 -DA:74,1 -DA:76,1 -DA:77,1 -DA:78,1 -DA:79,1 -DA:80,1 -DA:81,1 -DA:82,1 -DA:83,1 -DA:84,1 -DA:85,1 -DA:87,1 -DA:88,1 -DA:89,1 -DA:90,1 -DA:91,1 -DA:92,1 -DA:93,1 -DA:94,1 -DA:95,1 -LF:32 -LH:32 -end_of_record -SF:lib/src/utils/gradient_utils.dart -DA:8,0 -DA:13,0 -DA:14,0 -DA:15,0 -DA:16,0 -DA:17,0 -DA:21,0 -DA:22,0 -DA:26,0 -DA:28,0 -DA:29,0 -DA:30,0 -DA:31,0 -DA:35,0 -DA:36,0 -DA:38,0 -DA:39,0 -DA:40,0 -DA:41,0 -DA:45,0 -LF:20 -LH:0 -end_of_record diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies deleted file mode 100644 index 4df3bd1..0000000 --- a/example/.flutter-plugins-dependencies +++ /dev/null @@ -1 +0,0 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"/Users/yadaniyil/flutter/packages/integration_test/","native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"/Users/yadaniyil/flutter/packages/integration_test/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2023-08-28 11:18:31.970160","version":"3.13.1"} \ No newline at end of file diff --git a/example/.gitignore b/example/.gitignore index dce2403..79f7eca 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -1,14 +1,16 @@ # Miscellaneous *.class -*.lock *.log *.pyc *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ +migrate_working_dir/ # IntelliJ related *.iml @@ -16,72 +18,31 @@ *.iws .idea/ -# Visual Studio Code related -.vscode/ - -# Flutter repo-specific -/bin/cache/ -/bin/mingit/ -/dev/benchmarks/mega_gallery/ -/dev/bots/.recipe_deps -/dev/bots/android_tools/ -/dev/docs/doc/ -/dev/docs/lib/ -/dev/docs/pubspec.yaml -/packages/flutter/coverage/ -version +# 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 -.packages +.flutter-plugins-dependencies .pub-cache/ .pub/ -build/ -flutter_*.png -linked_*.ds -unlinked.ds -unlinked_spec.ds +/build/ +/coverage/ + +# Symbolication related +app.*.symbols -# Android related -#**/android/**/gradle-wrapper.jar -**/android/.gradle -**/android/captures/ -#**/android/gradlew -#**/android/gradlew.bat -**/android/local.properties -**/android/**/GeneratedPluginRegistrant.java +# Obfuscation related +app.*.map.json -# iOS/XCode related -**/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/App.framework -**/ios/Flutter/Flutter.framework -**/ios/Flutter/Generated.xcconfig -**/ios/Flutter/app.flx -**/ios/Flutter/app.zip -**/ios/Flutter/flutter_assets/ -**/ios/ServiceDefinitions.json -**/ios/Runner/GeneratedPluginRegistrant.* +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release -# Exceptions to above rules. -!**/ios/**/default.mode1v3 -!**/ios/**/default.mode2v3 -!**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages \ No newline at end of file +# Widget Preview related +.widget_preview/ diff --git a/example/.metadata b/example/.metadata index 82403ed..a54e41f 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,5 +4,33 @@ # This file should be version controlled and should not be manually edited. version: - revision: c7ea3ca377e909469c68f2ab878a5bc53d3cf66b - channel: beta + revision: "d3b14c876900e553bc736ca19295fc09e3853e8e" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: d3b14c876900e553bc736ca19295fc09e3853e8e + base_revision: d3b14c876900e553bc736ca19295fc09e3853e8e + - platform: android + create_revision: d3b14c876900e553bc736ca19295fc09e3853e8e + base_revision: d3b14c876900e553bc736ca19295fc09e3853e8e + - platform: ios + create_revision: d3b14c876900e553bc736ca19295fc09e3853e8e + base_revision: d3b14c876900e553bc736ca19295fc09e3853e8e + - platform: windows + create_revision: d3b14c876900e553bc736ca19295fc09e3853e8e + base_revision: d3b14c876900e553bc736ca19295fc09e3853e8e + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/example/README.md b/example/README.md index 64a12f6..cfab0dd 100644 --- a/example/README.md +++ b/example/README.md @@ -1,8 +1,7 @@ -# example +# badges example -A new Flutter project. +A demo app for the [badges](https://pub.dev/packages/badges) package. It shows badges on +app bar icons, tabs, the bottom navigation bar and avatars, plus the Twitter and Instagram +shapes, gradients and the slide animation on the cart badge (tap + / - to change it). -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.io/). +Run it with `flutter run` from this `example/` directory (Android, iOS or Windows). diff --git a/example/android/.gitignore b/example/android/.gitignore index 6f56801..be3943c 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -5,9 +5,10 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java +.cxx/ # Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +# See https://flutter.dev/to/reference-keystore key.properties **/*.keystore **/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index 5fe3c92..0000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -1,68 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion flutter.compileSdkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.example" - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts new file mode 100644 index 0000000..74caf01 --- /dev/null +++ b/example/android/app/build.gradle.kts @@ -0,0 +1,49 @@ +plugins { + id("com.android.application") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.example.example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.example" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + // Uses the version code from pubspec.yaml. When using split APKs, 1000 * ABI_VERSION + // is added automatically by Flutter. (https://developer.android.com/studio/build/configure-apk-splits#configure-APK-versions) + // You can force using the value of versionCode by specifying the `-P force-version-code-ignoring-abi=true` + // flag during build. + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } +} + +flutter { + source = "../.." +} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index c208884..399f698 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,6 +1,6 @@ - - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 3f41384..74a78b9 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - - + @@ -8,6 +7,7 @@ android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" + android:taskAffinity="" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" @@ -31,4 +31,15 @@ android:name="flutterEmbedding" android:value="2" /> + + + + + + + diff --git a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt index e793a00..ac81bae 100644 --- a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt +++ b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -2,5 +2,4 @@ package com.example.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() { -} +class MainActivity : FlutterActivity() diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml index 3db14bb..06952be 100644 --- a/example/android/app/src/main/res/values-night/styles.xml +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -3,7 +3,7 @@ diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index 4256f91..0000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.6.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/example/android/build.gradle.kts b/example/android/build.gradle.kts new file mode 100644 index 0000000..dbee657 --- /dev/null +++ b/example/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 94adc3a..e96108c 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,6 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true -android.enableJetifier=true +# This newDsl flag was added by the Flutter template +android.newDsl=false +# This builtInKotlin flag was added by the Flutter template +android.builtInKotlin=false diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58a..a20f2c4 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index 44e62bc..0000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/android/settings.gradle.kts b/example/android/settings.gradle.kts new file mode 100644 index 0000000..b28021a --- /dev/null +++ b/example/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "9.1.0" apply false + id("org.jetbrains.kotlin.android") version "2.4.0" apply false +} + +include(":app") diff --git a/example/coverage/lcov.info b/example/coverage/lcov.info deleted file mode 100644 index e69de29..0000000 diff --git a/example/ios/.gitignore b/example/ios/.gitignore index c096a2d..7a7f987 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -1,46 +1,34 @@ -.idea/ -.vagrant/ -.sconsign.dblite -.svn/ - -.DS_Store -*.swp -profile - -DerivedData/ -build/ -GeneratedPluginRegistrant.h -GeneratedPluginRegistrant.m - -.generated/ - -*.pbxuser +**/dgph *.mode1v3 *.mode2v3 +*.moved-aside +*.pbxuser *.perspectivev3 - -!default.pbxuser +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. !default.mode1v3 !default.mode2v3 +!default.pbxuser !default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - -/Flutter/app.flx -/Flutter/app.zip -/Flutter/flutter_assets/ -/Flutter/App.framework -/Flutter/Flutter.framework -/Flutter/Generated.xcconfig -/ServiceDefinitions.json -/Flutter/flutter_export_environment.sh - -Pods/ -.symlinks/ diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 9625e10..391a902 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -20,7 +20,5 @@ ???? CFBundleVersion 1.0 - MinimumOSVersion - 11.0 diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig index ec97fc6..592ceee 100644 --- a/example/ios/Flutter/Debug.xcconfig +++ b/example/ios/Flutter/Debug.xcconfig @@ -1,2 +1 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Flutter/Flutter.podspec b/example/ios/Flutter/Flutter.podspec deleted file mode 100644 index 29758b7..0000000 --- a/example/ios/Flutter/Flutter.podspec +++ /dev/null @@ -1,18 +0,0 @@ -# -# This podspec is NOT to be published. It is only used as a local source! -# This is a generated file; do not edit or check into version control. -# - -Pod::Spec.new do |s| - s.name = 'Flutter' - s.version = '1.0.0' - s.summary = 'A UI toolkit for beautiful and fast apps.' - s.homepage = 'https://flutter.dev' - s.license = { :type => 'BSD' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '11.0' - # Framework linking is handled by Flutter tooling, not CocoaPods. - # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. - s.vendored_frameworks = 'path/to/nothing' -end diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig index c4855bf..592ceee 100644 --- a/example/ios/Flutter/Release.xcconfig +++ b/example/ios/Flutter/Release.xcconfig @@ -1,2 +1 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Podfile b/example/ios/Podfile deleted file mode 100644 index d207307..0000000 --- a/example/ios/Podfile +++ /dev/null @@ -1,38 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '11.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 1c9a969..25942dc 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,17 +8,26 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; - 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; - 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - D40517F33D6344040707842E /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DD1FA343129FC7FF5F84D57 /* libPods-Runner.a */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -35,17 +44,17 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3869DA7265273353387147B5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 7AEFC2AAAD1E41FEC8612C57 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 7DD1FA343129FC7FF5F84D57 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -57,26 +66,25 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D40517F33D6344040707842E /* libPods-Runner.a in Frameworks */, + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 040983FC55ACDB1E2FD95D0D /* Pods */ = { + 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( - 7AEFC2AAAD1E41FEC8612C57 /* Pods-Runner.debug.xcconfig */, - 3869DA7265273353387147B5 /* Pods-Runner.release.xcconfig */, + 331C807B294A618700263BE5 /* RunnerTests.swift */, ); - name = Pods; - path = Pods; + path = RunnerTests; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, @@ -91,8 +99,7 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 040983FC55ACDB1E2FD95D0D /* Pods */, - 985BF9FFF7CA805928065D6C /* Frameworks */, + 331C8082294A63A400263BE5 /* RunnerTests */, ); sourceTree = ""; }; @@ -100,6 +107,7 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -107,43 +115,43 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 97C146F21CF9000F007C117D /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 985BF9FFF7CA805928065D6C /* Frameworks */ = { - isa = PBXGroup; - children = ( - 7DD1FA343129FC7FF5F84D57 /* libPods-Runner.a */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 57FBAE60819E5B5ADE59FB98 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, @@ -156,6 +164,9 @@ dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -166,43 +177,56 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; - ORGANIZATIONNAME = "The Chromium Authors"; + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = 2B9L42U8WK; + LastSwiftMigration = 1100; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); @@ -227,28 +251,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 57FBAE60819E5B5ADE59FB98 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -267,18 +269,34 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, - 97C146F31CF9000F007C117D /* main.m in Sources */, + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -299,10 +317,133 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 2B9L42U8WK; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -312,12 +453,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -330,6 +473,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -344,10 +488,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -356,6 +501,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -365,12 +511,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -383,6 +531,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -391,9 +540,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -404,24 +557,20 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = 1; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 2B9L42U8WK; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -431,24 +580,19 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = 1; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 2B9L42U8WK; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; @@ -456,11 +600,22 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( 97C147031CF9000F007C117D /* Debug */, 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -470,11 +625,26 @@ buildConfigurations = ( 97C147061CF9000F007C117D /* Debug */, 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 327eb3b..c3fedb2 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + - - - - + + + + + + @@ -63,11 +91,9 @@ ReferencedContainer = "container:Runner.xcodeproj"> - - - - diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/AppDelegate.h b/example/ios/Runner/AppDelegate.h deleted file mode 100644 index 36e21bb..0000000 --- a/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 59a72e9..0000000 --- a/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,13 +0,0 @@ -#include "AppDelegate.h" -#include "GeneratedPluginRegistrant.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - // Override point for customization after application launch. - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -@end diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..c30b367 --- /dev/null +++ b/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,16 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 3d43d11..dc9ada4 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index 28c6bf0..7353c41 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index 2ccbfd9..797d452 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index f091b6b..6ed2d93 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 4cde121..4cd7b00 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index d0ef06e..fe73094 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index dcdc230..321773c 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index 2ccbfd9..797d452 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index c8f9ed8..502f463 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index a6d6b86..0ec3034 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index a6d6b86..0ec3034 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index 75b2d16..e9f5fea 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index c4df70d..84ac32a 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index 6a84f41..8953cba 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index d0e1f58..0467bf1 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 8e5ff92..cd88d65 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -2,8 +2,12 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,13 +19,36 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + $(FLUTTER_BUILD_NAME) CFBundleSignature ???? CFBundleVersion - 1 + $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -39,11 +66,5 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIViewControllerBasedStatusBarAppearance - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/example/ios/Runner/SceneDelegate.swift b/example/ios/Runner/SceneDelegate.swift new file mode 100644 index 0000000..b9ce8ea --- /dev/null +++ b/example/ios/Runner/SceneDelegate.swift @@ -0,0 +1,6 @@ +import Flutter +import UIKit + +class SceneDelegate: FlutterSceneDelegate { + +} diff --git a/example/ios/Runner/main.m b/example/ios/Runner/main.m deleted file mode 100644 index dff6597..0000000 --- a/example/ios/Runner/main.m +++ /dev/null @@ -1,9 +0,0 @@ -#import -#import -#import "AppDelegate.h" - -int main(int argc, char* argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/example/ios/RunnerTests/RunnerTests.swift b/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/example/lib/test_screen.dart b/example/lib/test_screen.dart index 95503d1..20e5862 100644 --- a/example/lib/test_screen.dart +++ b/example/lib/test_screen.dart @@ -46,378 +46,405 @@ class _TestScreenState extends State { return Scaffold( appBar: AppBar(title: Text('Test Badge Screen')), body: Padding( - padding: const EdgeInsets.symmetric(vertical: 30.0), + padding: const EdgeInsets.symmetric(vertical: 30.0, horizontal: 12.0), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ /// Circle Badge with button - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: ElevatedButton( - onPressed: () => setState(() { - _showCircleBadge = !_showCircleBadge; - }), - child: Text('Circle size'), - ), - ), - badges.Badge( - key: const Key('CircleBadge'), - badgeAnimation: badges.BadgeAnimation.size( - disappearanceFadeAnimationDuration: Duration( - milliseconds: 200, - ), - curve: Curves.easeInCubic, - ), - showBadge: _showCircleBadge, - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.circle, - badgeColor: Colors.green, + // Scales the row down on narrow phones instead of overflowing. + FittedBox( + fit: BoxFit.scaleDown, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: ElevatedButton( + onPressed: () => setState(() { + _showCircleBadge = !_showCircleBadge; + }), + child: Text('Circle size'), + ), ), - badgeContent: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'circle', - style: TextStyle(color: Colors.white), + badges.Badge( + key: const Key('CircleBadge'), + badgeAnimation: badges.BadgeAnimation.size( + disappearanceFadeAnimationDuration: Duration( + milliseconds: 200, + ), + curve: Curves.easeInCubic, + ), + showBadge: _showCircleBadge, + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.circle, + badgeColor: Colors.green, + ), + badgeContent: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'circle', + style: TextStyle(color: Colors.white), + ), ), ), - ), - /// Square Badge with button - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: ElevatedButton( - onPressed: () => setState(() { - _showSquareBadge = !_showSquareBadge; - }), - child: Text('Square fade'), - ), - ), - badges.Badge( - key: const Key('SquareBadge'), - badgeAnimation: badges.BadgeAnimation.fade( - disappearanceFadeAnimationDuration: Duration( - milliseconds: 200, - ), - curve: Curves.easeInCubic, - ), - showBadge: _showSquareBadge, - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.square, - badgeColor: Colors.black, + /// Square Badge with button + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: ElevatedButton( + onPressed: () => setState(() { + _showSquareBadge = !_showSquareBadge; + }), + child: Text('Square fade'), + ), ), - badgeContent: Padding( - padding: const EdgeInsets.all(8.0), - child: Text('1111', style: TextStyle(color: Colors.white)), + badges.Badge( + key: const Key('SquareBadge'), + badgeAnimation: badges.BadgeAnimation.fade( + disappearanceFadeAnimationDuration: Duration( + milliseconds: 200, + ), + curve: Curves.easeInCubic, + ), + showBadge: _showSquareBadge, + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.square, + badgeColor: Colors.black, + ), + badgeContent: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + '1111', + style: TextStyle(color: Colors.white), + ), + ), ), - ), - ], + ], + ), ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - /// Twitter Badge with button - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: ElevatedButton( - onPressed: () => setState(() { - _showTwitterBadge = !_showTwitterBadge; - }), - child: Text('Twitter rotation'), - ), - ), - badges.Badge( - key: const Key('TwitterBadge'), - badgeAnimation: badges.BadgeAnimation.rotation( - disappearanceFadeAnimationDuration: Duration( - milliseconds: 200, - ), - curve: Curves.easeInCubic, - ), - showBadge: _showTwitterBadge, - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.twitter, - badgeColor: Colors.blue, + // Scales the row down on narrow phones instead of overflowing. + FittedBox( + fit: BoxFit.scaleDown, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + /// Twitter Badge with button + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: ElevatedButton( + onPressed: () => setState(() { + _showTwitterBadge = !_showTwitterBadge; + }), + child: Text('Twitter rotation'), + ), ), - badgeContent: Padding( - padding: const EdgeInsets.all(8.0), - child: Icon(Icons.check, color: Colors.white), + badges.Badge( + key: const Key('TwitterBadge'), + badgeAnimation: badges.BadgeAnimation.rotation( + disappearanceFadeAnimationDuration: Duration( + milliseconds: 200, + ), + curve: Curves.easeInCubic, + ), + showBadge: _showTwitterBadge, + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.twitter, + badgeColor: Colors.blue, + ), + badgeContent: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon(Icons.check, color: Colors.white), + ), ), - ), - /// Instagram Badge with button - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: ElevatedButton( - onPressed: () => setState(() { - _showInstagramBadge = !_showInstagramBadge; - }), - child: Text('Instagram scale'), - ), - ), - badges.Badge( - key: const Key('InstagramBadge'), - badgeAnimation: badges.BadgeAnimation.scale( - disappearanceFadeAnimationDuration: Duration( - milliseconds: 200, - ), - curve: Curves.easeInCubic, - ), - showBadge: _showInstagramBadge, - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.instagram, - badgeColor: Colors.red, + /// Instagram Badge with button + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: ElevatedButton( + onPressed: () => setState(() { + _showInstagramBadge = !_showInstagramBadge; + }), + child: Text('Instagram scale'), + ), ), - badgeContent: Padding( - padding: const EdgeInsets.all(8.0), - child: Icon(Icons.camera, color: Colors.white), + badges.Badge( + key: const Key('InstagramBadge'), + badgeAnimation: badges.BadgeAnimation.scale( + disappearanceFadeAnimationDuration: Duration( + milliseconds: 200, + ), + curve: Curves.easeInCubic, + ), + showBadge: _showInstagramBadge, + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.instagram, + badgeColor: Colors.red, + ), + badgeContent: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon(Icons.camera, color: Colors.white), + ), ), - ), - ], + ], + ), ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - /// Change Color Badge with button - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: ElevatedButton( - onPressed: () => setState(() { - if (_changeBadgeColor == Colors.purple) { - _changeBadgeColor = Colors.orange; - } else { - _changeBadgeColor = Colors.purple; - } - }), - child: Text('Change color'), - ), - ), - badges.Badge( - key: const Key('ChangeColorBadge'), - badgeAnimation: badges.BadgeAnimation.scale( - disappearanceFadeAnimationDuration: Duration( - milliseconds: 200, - ), - curve: Curves.easeInCubic, - ), - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.circle, - badgeColor: _changeBadgeColor, + // Scales the row down on narrow phones instead of overflowing. + FittedBox( + fit: BoxFit.scaleDown, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + /// Change Color Badge with button + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: ElevatedButton( + onPressed: () => setState(() { + if (_changeBadgeColor == Colors.purple) { + _changeBadgeColor = Colors.orange; + } else { + _changeBadgeColor = Colors.purple; + } + }), + child: Text('Change color'), + ), ), - badgeContent: Padding( - padding: const EdgeInsets.all(8.0), - child: Icon(Icons.ac_unit_sharp, color: Colors.white), + badges.Badge( + key: const Key('ChangeColorBadge'), + badgeAnimation: badges.BadgeAnimation.scale( + disappearanceFadeAnimationDuration: Duration( + milliseconds: 200, + ), + curve: Curves.easeInCubic, + ), + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.circle, + badgeColor: _changeBadgeColor, + ), + badgeContent: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon(Icons.ac_unit_sharp, color: Colors.white), + ), ), - ), - /// Change Icon Badge with button - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: ElevatedButton( - onPressed: () => setState(() { - if (_showChangeIconBadge) { - if (_badgeIcon == Icons.account_balance_rounded) { - _badgeIcon = Icons.add; - _showChangeIconBadge = !_showChangeIconBadge; + /// Change Icon Badge with button + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: ElevatedButton( + onPressed: () => setState(() { + if (_showChangeIconBadge) { + if (_badgeIcon == Icons.account_balance_rounded) { + _badgeIcon = Icons.add; + _showChangeIconBadge = !_showChangeIconBadge; + } else { + _badgeIcon = Icons.account_balance_rounded; + _showChangeIconBadge = !_showChangeIconBadge; + } } else { - _badgeIcon = Icons.account_balance_rounded; _showChangeIconBadge = !_showChangeIconBadge; } - } else { - _showChangeIconBadge = !_showChangeIconBadge; - } - }), - child: Text('Change icon'), - ), - ), - badges.Badge( - key: const Key('ChangeIconBadge'), - badgeAnimation: badges.BadgeAnimation.scale( - appearanceDisappearanceFadeAnimationEnabled: false, - disappearanceFadeAnimationDuration: Duration( - milliseconds: 200, - ), - curve: Curves.easeInCubic, + }), + child: Text('Change icon'), + ), ), - showBadge: _showChangeIconBadge, - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.circle, - badgeColor: Colors.yellow, + badges.Badge( + key: const Key('ChangeIconBadge'), + badgeAnimation: badges.BadgeAnimation.scale( + appearanceDisappearanceFadeAnimationEnabled: false, + disappearanceFadeAnimationDuration: Duration( + milliseconds: 200, + ), + curve: Curves.easeInCubic, + ), + showBadge: _showChangeIconBadge, + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.circle, + badgeColor: Colors.yellow, + ), + badgeContent: Icon(_badgeIcon, color: Colors.white), ), - badgeContent: Icon(_badgeIcon, color: Colors.white), - ), - ], + ], + ), ), /// Change Amount Badge with button - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton.icon( - onPressed: () => setState(() { - _badgeAmount++; - }), - icon: Icon(Icons.add), - label: Text('Add'), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: ElevatedButton.icon( - onPressed: _badgeAmount != 0 - ? () => setState(() { - _badgeAmount--; - }) - : null, - icon: Icon(Icons.remove), - label: Text('Remove'), - ), - ), - badges.Badge( - key: const Key('ChangeAmountBadge'), - badgeAnimation: badges.BadgeAnimation.slide( - toAnimate: false, - colorChangeAnimationDuration: Duration(milliseconds: 200), - curve: Curves.easeInCubic, - ), - showBadge: _showBadgeAmount, - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.circle, - badgeColor: Colors.pink, - ), - badgeContent: Text(_badgeAmount.toString()), - ), - ], - ), - - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - /// Animation Duration More Appearance Duration - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: ElevatedButton( + // Scales the row down on narrow phones instead of overflowing. + FittedBox( + fit: BoxFit.scaleDown, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ElevatedButton.icon( onPressed: () => setState(() { - _showAnimMoreAppear = !_showAnimMoreAppear; + _badgeAmount++; }), - child: Text('anim>appear'), - ), - ), - badges.Badge( - showBadge: _showAnimMoreAppear, - key: const Key('AnimationMoreAppearance'), - badgeAnimation: badges.BadgeAnimation.scale( - animationDuration: Duration(milliseconds: 500), - disappearanceFadeAnimationDuration: Duration( - milliseconds: 200, - ), - curve: Curves.easeInCubic, - ), - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.circle, - badgeColor: Colors.green, + icon: Icon(Icons.add), + label: Text('Add'), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: ElevatedButton.icon( + onPressed: _badgeAmount != 0 + ? () => setState(() { + _badgeAmount--; + }) + : null, + icon: Icon(Icons.remove), + label: Text('Remove'), + ), ), - badgeContent: Padding( - padding: const EdgeInsets.all(8.0), - child: Icon(Icons.cloud, color: Colors.white), + badges.Badge( + key: const Key('ChangeAmountBadge'), + badgeAnimation: badges.BadgeAnimation.slide( + toAnimate: false, + colorChangeAnimationDuration: Duration(milliseconds: 200), + curve: Curves.easeInCubic, + ), + showBadge: _showBadgeAmount, + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.circle, + badgeColor: Colors.pink, + ), + badgeContent: Text(_badgeAmount.toString()), ), - ), + ], + ), + ), - /// Animation Duration Less Appearance Duration - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: ElevatedButton( - onPressed: () => setState(() { - _showAnimLessAppear = !_showAnimLessAppear; - }), - child: Text('anim setState(() { + _showAnimMoreAppear = !_showAnimMoreAppear; + }), + child: Text('anim>appear'), + ), ), - ), - badges.Badge( - key: const Key('AnimationLessAppearance'), - badgeAnimation: badges.BadgeAnimation.scale( - animationDuration: Duration(milliseconds: 200), - disappearanceFadeAnimationDuration: Duration( - milliseconds: 500, - ), - curve: Curves.easeInCubic, + badges.Badge( + showBadge: _showAnimMoreAppear, + key: const Key('AnimationMoreAppearance'), + badgeAnimation: badges.BadgeAnimation.scale( + animationDuration: Duration(milliseconds: 500), + disappearanceFadeAnimationDuration: Duration( + milliseconds: 200, + ), + curve: Curves.easeInCubic, + ), + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.circle, + badgeColor: Colors.green, + ), + badgeContent: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon(Icons.cloud, color: Colors.white), + ), ), - showBadge: _showAnimLessAppear, - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.circle, - badgeColor: Colors.purple, + + /// Animation Duration Less Appearance Duration + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: ElevatedButton( + onPressed: () => setState(() { + _showAnimLessAppear = !_showAnimLessAppear; + }), + child: Text('anim setState(() { - _showAnimZero = !_showAnimZero; - }), - child: Text('anim=zero'), - ), - ), - badges.Badge( - showBadge: _showAnimZero, - key: const Key('AnimationZero'), - badgeAnimation: badges.BadgeAnimation.scale( - animationDuration: Duration.zero, - disappearanceFadeAnimationDuration: Duration( - milliseconds: 200, - ), - curve: Curves.easeInCubic, - ), - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.circle, - badgeColor: Colors.black38, + // Scales the row down on narrow phones instead of overflowing. + FittedBox( + fit: BoxFit.scaleDown, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + /// Animation Duration Zero + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: ElevatedButton( + onPressed: () => setState(() { + _showAnimZero = !_showAnimZero; + }), + child: Text('anim=zero'), + ), ), - badgeContent: Padding( - padding: const EdgeInsets.all(8.0), - child: Icon(Icons.adb, color: Colors.white), + badges.Badge( + showBadge: _showAnimZero, + key: const Key('AnimationZero'), + badgeAnimation: badges.BadgeAnimation.scale( + animationDuration: Duration.zero, + disappearanceFadeAnimationDuration: Duration( + milliseconds: 200, + ), + curve: Curves.easeInCubic, + ), + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.circle, + badgeColor: Colors.black38, + ), + badgeContent: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon(Icons.adb, color: Colors.white), + ), ), - ), - /// Appearance Duration Zero - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: ElevatedButton( - onPressed: () => setState(() { - _showAppearZero = !_showAppearZero; - }), - child: Text('appear=zero'), - ), - ), - badges.Badge( - key: const Key('AppearanceZero'), - badgeAnimation: badges.BadgeAnimation.scale( - animationDuration: Duration(milliseconds: 200), - disappearanceFadeAnimationDuration: Duration.zero, - curve: Curves.easeInCubic, - ), - showBadge: _showAppearZero, - badgeStyle: badges.BadgeStyle( - shape: badges.BadgeShape.circle, - badgeColor: Colors.brown, + /// Appearance Duration Zero + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: ElevatedButton( + onPressed: () => setState(() { + _showAppearZero = !_showAppearZero; + }), + child: Text('appear=zero'), + ), ), - badgeContent: Padding( - padding: const EdgeInsets.all(8.0), - child: Icon(Icons.access_time, color: Colors.white), + badges.Badge( + key: const Key('AppearanceZero'), + badgeAnimation: badges.BadgeAnimation.scale( + animationDuration: Duration(milliseconds: 200), + disappearanceFadeAnimationDuration: Duration.zero, + curve: Curves.easeInCubic, + ), + showBadge: _showAppearZero, + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.circle, + badgeColor: Colors.brown, + ), + badgeContent: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon(Icons.access_time, color: Colors.white), + ), ), - ), - ], + ], + ), ), ], ), diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 6cb1d9c..124d1e7 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,5 +1,7 @@ name: example -description: A new Flutter project. +description: Demo app for the badges package. +publish_to: 'none' +version: 1.0.0+1 environment: sdk: ">=3.13.0 <4.0.0" diff --git a/example/windows/CMakeLists.txt b/example/windows/CMakeLists.txt index 55ab02e..d960948 100644 --- a/example/windows/CMakeLists.txt +++ b/example/windows/CMakeLists.txt @@ -1,13 +1,16 @@ +# Project-level configuration. cmake_minimum_required(VERSION 3.14) -project(examlpe LANGUAGES CXX) +project(example LANGUAGES CXX) -set(BINARY_NAME "examlpe") +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "example") -cmake_policy(SET CMP0063 NEW) +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Configure build options. +# Define build configuration option. get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(IS_MULTICONFIG) set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" @@ -20,7 +23,7 @@ else() "Debug" "Profile" "Release") endif() endif() - +# Define settings for the Profile build mode. set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") @@ -30,6 +33,10 @@ set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") add_definitions(-DUNICODE -D_UNICODE) # Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. function(APPLY_STANDARD_SETTINGS TARGET) target_compile_features(${TARGET} PUBLIC cxx_std_17) target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") @@ -38,14 +45,14 @@ function(APPLY_STANDARD_SETTINGS TARGET) target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") endfunction() -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - # Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") add_subdirectory(${FLUTTER_MANAGED_DIR}) -# Application build +# Application build; see runner/CMakeLists.txt. add_subdirectory("runner") + # Generated plugin build rules, which manage building the plugins and adding # them to the application. include(flutter/generated_plugins.cmake) @@ -80,6 +87,12 @@ if(PLUGIN_BUNDLED_LIBRARIES) COMPONENT Runtime) endif() +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") diff --git a/example/windows/flutter/CMakeLists.txt b/example/windows/flutter/CMakeLists.txt index b2e4bd8..903f489 100644 --- a/example/windows/flutter/CMakeLists.txt +++ b/example/windows/flutter/CMakeLists.txt @@ -1,3 +1,4 @@ +# This file controls Flutter-level build steps. It should not be edited. cmake_minimum_required(VERSION 3.14) set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") @@ -9,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/example/windows/runner/CMakeLists.txt b/example/windows/runner/CMakeLists.txt index de2d891..394917c 100644 --- a/example/windows/runner/CMakeLists.txt +++ b/example/windows/runner/CMakeLists.txt @@ -1,6 +1,11 @@ cmake_minimum_required(VERSION 3.14) project(runner LANGUAGES CXX) +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. add_executable(${BINARY_NAME} WIN32 "flutter_window.cpp" "main.cpp" @@ -10,8 +15,26 @@ add_executable(${BINARY_NAME} WIN32 "Runner.rc" "runner.exe.manifest" ) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/example/windows/runner/Runner.rc b/example/windows/runner/Runner.rc index 7bb917a..0e3e27b 100644 --- a/example/windows/runner/Runner.rc +++ b/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif @@ -90,12 +90,12 @@ BEGIN BLOCK "040904e4" BEGIN VALUE "CompanyName", "com.example" "\0" - VALUE "FileDescription", "examlpe" "\0" + VALUE "FileDescription", "example" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "examlpe" "\0" - VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" - VALUE "OriginalFilename", "examlpe.exe" "\0" - VALUE "ProductName", "examlpe" "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" END END diff --git a/example/windows/runner/flutter_window.cpp b/example/windows/runner/flutter_window.cpp index b43b909..955ee30 100644 --- a/example/windows/runner/flutter_window.cpp +++ b/example/windows/runner/flutter_window.cpp @@ -26,6 +26,16 @@ bool FlutterWindow::OnCreate() { } RegisterPlugins(flutter_controller_->engine()); SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + return true; } diff --git a/example/windows/runner/main.cpp b/example/windows/runner/main.cpp index 5a0b3ea..a61bf80 100644 --- a/example/windows/runner/main.cpp +++ b/example/windows/runner/main.cpp @@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"examlpe", origin, size)) { + if (!window.Create(L"example", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true); diff --git a/example/windows/runner/runner.exe.manifest b/example/windows/runner/runner.exe.manifest index c977c4a..153653e 100644 --- a/example/windows/runner/runner.exe.manifest +++ b/example/windows/runner/runner.exe.manifest @@ -7,14 +7,8 @@ - + - - - - - - diff --git a/example/windows/runner/utils.cpp b/example/windows/runner/utils.cpp index d19bdbb..3cb7146 100644 --- a/example/windows/runner/utils.cpp +++ b/example/windows/runner/utils.cpp @@ -45,18 +45,23 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) { if (utf16_string == nullptr) { return std::string(); } + // First, find the length of the string with a safe upper bound (CWE-126). + // UNICODE_STRING_MAX_CHARS (32767) is the maximum length of a UNICODE_STRING. + int input_length = static_cast(wcsnlen(utf16_string, UNICODE_STRING_MAX_CHARS)); + // Now use that bounded length to determine the required buffer size. + // When an explicit length is passed, WideCharToMultiByte does not include + // the null terminator in its returned size. int target_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); - if (target_length == 0) { - return std::string(); - } + input_length, nullptr, 0, nullptr, nullptr); std::string utf8_string; + if (target_length == 0 || static_cast(target_length) > utf8_string.max_size()) { + return utf8_string; + } utf8_string.resize(target_length); int converted_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); + input_length, utf8_string.data(), target_length, nullptr, nullptr); if (converted_length == 0) { return std::string(); } diff --git a/example/windows/runner/win32_window.cpp b/example/windows/runner/win32_window.cpp index c10f08d..60608d0 100644 --- a/example/windows/runner/win32_window.cpp +++ b/example/windows/runner/win32_window.cpp @@ -1,13 +1,31 @@ #include "win32_window.h" +#include #include #include "resource.h" namespace { +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + // The number of Win32Window objects that currently exist. static int g_active_window_count = 0; @@ -31,8 +49,8 @@ void EnableFullDpiSupportIfAvailable(HWND hwnd) { GetProcAddress(user32_module, "EnableNonClientDpiScaling")); if (enable_non_client_dpi_scaling != nullptr) { enable_non_client_dpi_scaling(hwnd); - FreeLibrary(user32_module); } + FreeLibrary(user32_module); } } // namespace @@ -42,7 +60,7 @@ class WindowClassRegistrar { public: ~WindowClassRegistrar() = default; - // Returns the singleton registar instance. + // Returns the singleton registrar instance. static WindowClassRegistrar* GetInstance() { if (!instance_) { instance_ = new WindowClassRegistrar(); @@ -102,9 +120,9 @@ Win32Window::~Win32Window() { Destroy(); } -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { Destroy(); const wchar_t* window_class = @@ -117,7 +135,7 @@ bool Win32Window::CreateAndShow(const std::wstring& title, double scale_factor = dpi / 96.0; HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), Scale(size.width, scale_factor), Scale(size.height, scale_factor), nullptr, nullptr, GetModuleHandle(nullptr), this); @@ -126,9 +144,15 @@ bool Win32Window::CreateAndShow(const std::wstring& title, return false; } + UpdateTheme(window); + return OnCreate(); } +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + // static LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, @@ -188,6 +212,10 @@ Win32Window::MessageHandler(HWND hwnd, SetFocus(child_content_); } return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; } return DefWindowProc(window_handle_, message, wparam, lparam); @@ -243,3 +271,18 @@ bool Win32Window::OnCreate() { void Win32Window::OnDestroy() { // No-op; provided for subclasses. } + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/example/windows/runner/win32_window.h b/example/windows/runner/win32_window.h index 17ba431..e901dde 100644 --- a/example/windows/runner/win32_window.h +++ b/example/windows/runner/win32_window.h @@ -28,15 +28,16 @@ class Win32Window { Win32Window(); virtual ~Win32Window(); - // Creates and shows a win32 window with |title| and position and size using + // Creates a win32 window with |title| that is positioned and sized using // |origin| and |size|. New windows are created on the default monitor. Window // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size to will treat the width height passed in to this function - // as logical pixels and scale to appropriate for the default monitor. Returns - // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size); + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); // Release OS resources associated with window. void Destroy(); @@ -76,7 +77,7 @@ class Win32Window { // OS callback called by message pump. Handles the WM_NCCREATE message which // is passed when the non-client area is being created and enables automatic // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by + // responds to changes in DPI. All other messages are handled by // MessageHandler. static LRESULT CALLBACK WndProc(HWND const window, UINT const message, @@ -86,6 +87,9 @@ class Win32Window { // Retrieves a class instance pointer for |window| static Win32Window* GetThisFromHandle(HWND const window) noexcept; + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + bool quit_on_close_ = false; // window handle for top level window. diff --git a/lib/src/badge.dart b/lib/src/badge.dart index 9874fd9..caa3eeb 100644 --- a/lib/src/badge.dart +++ b/lib/src/badge.dart @@ -34,10 +34,12 @@ class Badge extends StatefulWidget { /// Content inside badge. final Widget? badgeContent; - /// Can make your [badgeContent] interactive. + /// Whether the badge ignores taps. /// The default value is false. - /// Make it true to make badge intercept all taps - /// Make it false and all taps will be passed through the badge + /// Keep it false so the badge receives taps ([onTap] and any interactive + /// [badgeContent] work). + /// Make it true and all taps will pass through the badge to the widgets + /// below it. [onTap] is not called then. final bool ignorePointer; /// Allows to edit fit parameter to [Stack] widget. @@ -50,7 +52,9 @@ class Badge extends StatefulWidget { /// Will be called when you tap on the badge /// Important: if the badge is outside of the child - /// the additional padding will be applied to make the full badge clickable + /// the additional padding will be applied to make the full badge clickable. + /// This padding makes the whole widget bigger, so it can move the widgets + /// around it. final Function()? onTap; @override @@ -103,12 +107,18 @@ class BadgeState extends State with TickerProviderStateMixin { } } + /// A hidden badge must not catch taps meant for the widgets below it. + Widget _getTappableBadge() { + return IgnorePointer( + ignoring: widget.ignorePointer || !widget.showBadge, + child: GestureDetector(onTap: widget.onTap, child: _getBadge()), + ); + } + @override Widget build(BuildContext context) { if (widget.child == null) { - return widget.ignorePointer - ? IgnorePointer(child: _getBadge()) - : GestureDetector(onTap: widget.onTap, child: _getBadge()); + return _getTappableBadge(); } else { return Stack( fit: widget.stackFit, @@ -128,9 +138,7 @@ class BadgeState extends State with TickerProviderStateMixin { position: widget.onTap == null ? widget.position : CalculationUtils.calculatePosition(widget.position), - child: widget.ignorePointer - ? IgnorePointer(child: _getBadge()) - : GestureDetector(onTap: widget.onTap, child: _getBadge()), + child: _getTappableBadge(), ), ], ); @@ -206,6 +214,22 @@ class BadgeState extends State with TickerProviderStateMixin { if (!widget.badgeAnimation.toAnimate) return; + // Animations were off, so the controllers never ran. Jump them to the + // end so the badge doesn't turn invisible. + if (!oldWidget.badgeAnimation.toAnimate) { + if (widget.showBadge) { + _animationController.value = 1; + _appearanceController.value = 1; + if (widget.badgeAnimation.loopAnimation && enableLoopAnimation) { + _animationController.repeat( + period: _animationController.duration, + reverse: true, + ); + } + } + return; + } + // --- showBadge changes are handled FIRST (fixes issue #114) --- // Badge became visible. @@ -357,49 +381,57 @@ class _BadgeVisual extends StatelessWidget { ); } + Widget container = AnimatedContainer( + curve: badgeAnimation.colorChangeAnimationCurve, + duration: badgeAnimation.toAnimate + ? badgeAnimation.colorChangeAnimationDuration + : Duration.zero, + decoration: badgeStyle.shape == BadgeShape.circle + ? BoxDecoration( + color: badgeStyle.badgeColor, + border: boxBorder, + gradient: badgeStyle.badgeGradient?.gradient(), + shape: BoxShape.circle, + ) + : BoxDecoration( + color: badgeStyle.badgeColor, + gradient: badgeStyle.badgeGradient?.gradient(), + shape: BoxShape.rectangle, + borderRadius: badgeStyle.borderRadius, + border: boxBorder, + ), + // IntrinsicWidth sizes the badge to its content's natural width. + child: IntrinsicWidth( + child: Padding(padding: badgeStyle.padding, child: badgeContent), + ), + ); + + // MaterialType.transparency never paints a shadow, so the elevation + // shadow is drawn here instead. + if (badgeStyle.elevation > 0) { + container = PhysicalShape( + clipper: ShapeBorderClipper(shape: materialShape), + elevation: badgeStyle.elevation, + color: Colors.transparent, + shadowColor: Colors.black, + child: container, + ); + } + return Material( shape: materialShape, - elevation: badgeStyle.elevation, // Without this Colors.transparent will be ignored type: MaterialType.transparency, - child: AnimatedContainer( - curve: badgeAnimation.colorChangeAnimationCurve, - duration: badgeAnimation.toAnimate - ? badgeAnimation.colorChangeAnimationDuration - : Duration.zero, - decoration: badgeStyle.shape == BadgeShape.circle - ? BoxDecoration( - color: badgeStyle.badgeColor, - border: boxBorder, - gradient: badgeStyle.badgeGradient?.gradient(), - shape: BoxShape.circle, - ) - : BoxDecoration( - color: badgeStyle.badgeColor, - gradient: badgeStyle.badgeGradient?.gradient(), - shape: BoxShape.rectangle, - borderRadius: badgeStyle.borderRadius, - border: boxBorder, - ), - // ConstrainedBox ensures the badge is at least as wide as it is tall, - // so single-character / small-icon badges stay circular (PR #111). - child: ConstrainedBox( - constraints: const BoxConstraints(minWidth: 0), - child: IntrinsicWidth( - child: Padding(padding: badgeStyle.padding, child: badgeContent), - ), - ), - ), + child: container, ); } @override Widget build(BuildContext context) { + // Listen to the controller directly: a new CurvedAnimation per build + // would add a listener to the controller every time and never remove it. final inner = AnimatedBuilder( - animation: CurvedAnimation( - parent: appearanceController, - curve: Curves.linear, - ), + animation: appearanceController, builder: (context, child) { return Opacity(opacity: _getOpacity(), child: child); }, diff --git a/lib/src/badge_position.dart b/lib/src/badge_position.dart index 36ea473..d0da1bc 100644 --- a/lib/src/badge_position.dart +++ b/lib/src/badge_position.dart @@ -16,7 +16,9 @@ class BadgePosition { /// Distance to the bottom side of a parent widget. final double? bottom; - /// Indicates that the badge is in center of parent widget + /// Indicates that the badge is centered in the parent widget. + /// If [start] or [end] is set, the badge is only centered vertically + /// and keeps that horizontal offset. final bool isCenter; /// This creates a new instance of this widget @@ -77,12 +79,12 @@ class BadgePosition { /// Factory method that creates a new instance of this widget /// vertically centered on the start (left) side. factory BadgePosition.centerStart({double start = -10}) { - return BadgePosition._(start: start); + return BadgePosition._(start: start, isCenter: true); } /// Factory method that creates a new instance of this widget /// vertically centered on the end (right) side. factory BadgePosition.centerEnd({double end = -10}) { - return BadgePosition._(end: end); + return BadgePosition._(end: end, isCenter: true); } } diff --git a/lib/src/badge_positioned.dart b/lib/src/badge_positioned.dart index 2d4f87e..7e7b010 100644 --- a/lib/src/badge_positioned.dart +++ b/lib/src/badge_positioned.dart @@ -31,6 +31,17 @@ class BadgePositioned extends StatelessWidget { ); } + if (position.isCenter && (position.start != null || position.end != null)) { + // centerStart / centerEnd: centered vertically, offset horizontally. + return PositionedDirectional( + top: 0, + bottom: 0, + start: position.start, + end: position.end, + child: Align(widthFactor: 1, child: child), + ); + } + if (position.isCenter) { return Positioned.fill( child: Align(alignment: Alignment.center, child: child), diff --git a/lib/src/badge_shape.dart b/lib/src/badge_shape.dart index d9b271b..869ed16 100644 --- a/lib/src/badge_shape.dart +++ b/lib/src/badge_shape.dart @@ -16,10 +16,10 @@ enum BadgeShape { square, /// To make the twitter badge . - /// See [TwitterBadgeShapePainter] for more details. + /// Drawn with a custom painter. twitter, /// To make the instagram badge . - /// See [InstagramBadgeShapePainter] for more details. + /// Drawn with a custom painter. instagram, } diff --git a/lib/src/badge_style.dart b/lib/src/badge_style.dart index b376998..1eab815 100644 --- a/lib/src/badge_style.dart +++ b/lib/src/badge_style.dart @@ -11,7 +11,7 @@ class BadgeStyle { final BorderRadius borderRadius; /// Background color of the badge. - /// If [gradient] is not null, this property will be ignored. + /// If [badgeGradient] is not null, this property will be ignored. final Color badgeColor; /// Allows to set border side to this [badgeContent]. @@ -19,6 +19,8 @@ class BadgeStyle { final BorderSide borderSide; /// The size of the shadow below the badge. + /// The default value is 0 (no shadow). + /// Ignored for [BadgeShape.twitter] and [BadgeShape.instagram]. final double elevation; /// Background gradient color of the badge. @@ -38,7 +40,7 @@ class BadgeStyle { this.borderRadius = BorderRadius.zero, this.badgeColor = Colors.red, this.borderSide = BorderSide.none, - this.elevation = 2, + this.elevation = 0, this.badgeGradient, this.borderGradient, this.padding = const EdgeInsets.all(5.0), diff --git a/lib/src/utils/calculation_utils.dart b/lib/src/utils/calculation_utils.dart index 83a00b1..f9068e9 100644 --- a/lib/src/utils/calculation_utils.dart +++ b/lib/src/utils/calculation_utils.dart @@ -1,5 +1,5 @@ import 'package:badges/badges.dart'; -import 'package:material_ui/material_ui.dart'; +import 'package:flutter/widgets.dart'; class CalculationUtils { /// When the onTap is specified the additional padding is added @@ -16,6 +16,14 @@ class CalculationUtils { return !digit.isNegative ? digit : 0; } + if (position.isCenter) { + return BadgePosition.custom( + start: getUpdatedPosition(position.start), + end: getUpdatedPosition(position.end), + isCenter: true, + ); + } + return BadgePosition.custom( start: getUpdatedPosition(position.start), end: getUpdatedPosition(position.end), @@ -26,13 +34,11 @@ class CalculationUtils { /// When the onTap is specified, we need to add some padding /// to make the full badge tappable. - static EdgeInsets calculatePadding(BadgePosition? position) { + /// The padding is directional because the badge is positioned with + /// start/end, so it stays correct in right-to-left layouts. + static EdgeInsetsDirectional calculatePadding(BadgePosition? position) { if (position == null) { - return const EdgeInsets.only(top: 8, right: 10); - } - - if (position.isCenter) { - return EdgeInsets.zero; + return const EdgeInsetsDirectional.only(top: 8, end: 10); } double getUpdatedPadding(double? digit) { @@ -42,17 +48,24 @@ class CalculationUtils { return digit.abs(); } + if (position.isCenter) { + return EdgeInsetsDirectional.only( + start: getUpdatedPadding(position.start), + end: getUpdatedPadding(position.end), + ); + } + if (position.top != null && position.start != null) { - return EdgeInsets.only( + return EdgeInsetsDirectional.only( top: getUpdatedPadding(position.top), - left: getUpdatedPadding(position.start), + start: getUpdatedPadding(position.start), ); } - return EdgeInsets.only( + return EdgeInsetsDirectional.only( top: getUpdatedPadding(position.top), bottom: getUpdatedPadding(position.bottom), - left: getUpdatedPadding(position.start), - right: getUpdatedPadding(position.end), + start: getUpdatedPadding(position.start), + end: getUpdatedPadding(position.end), ); } @@ -80,6 +93,10 @@ class CalculationUtils { } else if (alignment == Alignment.topRight) { return Offset(width * 0.809, height * 0.191); } - return Offset(width, height); + // Any other alignment (e.g. AlignmentDirectional) maps to its point in + // the badge rect. + return alignment + .resolve(TextDirection.ltr) + .withinRect(Offset.zero & Size(width, height)); } } diff --git a/lib/src/utils/gradient_utils.dart b/lib/src/utils/gradient_utils.dart index 67cb4ff..4127f3b 100644 --- a/lib/src/utils/gradient_utils.dart +++ b/lib/src/utils/gradient_utils.dart @@ -11,6 +11,9 @@ class GradientUtils { required double width, required double height, }) { + final colors = badgeGradient.colors; + final stops = badgeGradient.stops ?? _evenStops(colors.length); + final tileMode = badgeGradient.tileMode; switch (badgeGradient.gradientType) { case BadgeGradientType.linear: return ui.Gradient.linear( @@ -24,7 +27,9 @@ class GradientUtils { width: width, height: height, ), - badgeGradient.colors, + colors, + stops, + tileMode, ); case BadgeGradientType.radial: return ui.Gradient.radial( @@ -34,7 +39,9 @@ class GradientUtils { height: height, ), width * badgeGradient.radius!, - badgeGradient.colors, + colors, + stops, + tileMode, ); case BadgeGradientType.sweep: return ui.Gradient.sweep( @@ -43,8 +50,19 @@ class GradientUtils { width: width, height: height, ), - badgeGradient.colors, + colors, + stops, + tileMode, + badgeGradient.startAngle!, + badgeGradient.endAngle!, ); } } + + /// dart:ui gradients need explicit stops when there are more than two + /// colors, so spread them evenly like [Gradient.createShader] does. + static List? _evenStops(int count) { + if (count <= 2) return null; + return List.generate(count, (i) => i / (count - 1)); + } } diff --git a/pubspec.yaml b/pubspec.yaml index 012dd97..d9abe1d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,6 +3,11 @@ description: A package for creating badges. Badges can be used for an additional version: 4.0.0 repository: https://github.com/yako-dev/flutter_badges issue_tracker: https://github.com/yako-dev/flutter_badges/issues +topics: + - badge + - notification + - widget + - ui dependencies: flutter: diff --git a/test/badges_test.dart b/test/badges_test.dart index 2a06e0c..b5e9a70 100644 --- a/test/badges_test.dart +++ b/test/badges_test.dart @@ -488,7 +488,7 @@ void main() { expect(badgeWidget.badgeStyle.borderRadius, BorderRadius.zero); expect(badgeWidget.badgeStyle.badgeColor, Colors.red); expect(badgeWidget.badgeStyle.borderSide, BorderSide.none); - expect(badgeWidget.badgeStyle.elevation, 2); + expect(badgeWidget.badgeStyle.elevation, 0); expect(badgeWidget.badgeStyle.badgeGradient, null); expect(badgeWidget.badgeStyle.borderGradient, null); expect(badgeWidget.badgeStyle.padding, const EdgeInsets.all(5.0)); @@ -1190,6 +1190,293 @@ void main() { }, ); }); + + // --------------------------------------------------------------------------- + // Elevation (MaterialType.transparency ignores Material.elevation) + // --------------------------------------------------------------------------- + group('Elevation', () { + Finder shadowFinder() => find.descendant( + of: find.byType(badges.Badge), + matching: find.byType(PhysicalShape), + ); + + testWidgets('no shadow by default', (tester) async { + await tester.pumpWidget( + _wrapWithMaterialApp(const badges.Badge(badgeContent: Text('1'))), + ); + expect(shadowFinder(), findsNothing); + }); + + testWidgets('elevation draws a shadow', (tester) async { + await tester.pumpWidget( + _wrapWithMaterialApp( + const badges.Badge( + badgeContent: Text('1'), + badgeStyle: badges.BadgeStyle(elevation: 4), + ), + ), + ); + final shape = tester.widget(shadowFinder()); + expect(shape.elevation, 4); + expect(shape.color, Colors.transparent); + }); + + testWidgets('square shape shadow follows the border radius', ( + tester, + ) async { + final radius = BorderRadius.circular(6); + await tester.pumpWidget( + _wrapWithMaterialApp( + badges.Badge( + badgeContent: const Text('1'), + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.square, + borderRadius: radius, + elevation: 2, + ), + ), + ), + ); + final clipper = + tester.widget(shadowFinder()).clipper + as ShapeBorderClipper; + expect(clipper.shape, RoundedRectangleBorder(borderRadius: radius)); + }); + }); + + // --------------------------------------------------------------------------- + // Positioning with onTap + // --------------------------------------------------------------------------- + group('Positioning', () { + const childKey = Key('child'); + const contentKey = Key('content'); + + Widget badgeWith(badges.BadgePosition position, {VoidCallback? onTap}) { + return Center( + child: badges.Badge( + position: position, + onTap: onTap, + badgeAnimation: const badges.BadgeAnimation.fade(toAnimate: false), + badgeContent: const SizedBox(key: contentKey, width: 10, height: 10), + child: const SizedBox(key: childKey, width: 100, height: 100), + ), + ); + } + + Offset contentCenterInChild(WidgetTester tester) { + return tester.getCenter(find.byKey(contentKey)) - + tester.getTopLeft(find.byKey(childKey)); + } + + testWidgets('center() stays centered when onTap is set', (tester) async { + await tester.pumpWidget( + _wrapWithMaterialApp( + badgeWith(badges.BadgePosition.center(), onTap: () {}), + ), + ); + expect(contentCenterInChild(tester), const Offset(50, 50)); + }); + + for (final withTap in [false, true]) { + testWidgets('centerStart() is vertically centered (onTap: $withTap)', ( + tester, + ) async { + await tester.pumpWidget( + _wrapWithMaterialApp( + badgeWith( + badges.BadgePosition.centerStart(), + onTap: withTap ? () {} : null, + ), + ), + ); + final center = contentCenterInChild(tester); + expect(center.dy, 50); + // Badge box is 20 wide (10 + 2 * 5 padding), 10 px left of the child. + expect(center.dx, 0); + }); + + testWidgets('centerEnd() is vertically centered (onTap: $withTap)', ( + tester, + ) async { + await tester.pumpWidget( + _wrapWithMaterialApp( + badgeWith( + badges.BadgePosition.centerEnd(), + onTap: withTap ? () {} : null, + ), + ), + ); + final center = contentCenterInChild(tester); + expect(center.dy, 50); + expect(center.dx, 100); + }); + } + + testWidgets('onTap does not move the badge in right-to-left layouts', ( + tester, + ) async { + Future measure({VoidCallback? onTap}) async { + await tester.pumpWidget( + MaterialApp( + home: Directionality( + textDirection: TextDirection.rtl, + child: Scaffold( + body: badgeWith(badges.BadgePosition.topEnd(), onTap: onTap), + ), + ), + ), + ); + return contentCenterInChild(tester); + } + + final withoutTap = await measure(); + final withTap = await measure(onTap: () {}); + expect(withTap, withoutTap); + // topEnd in RTL is the top-left corner: the 20 px wide badge sits + // 10 px past the child's left edge. + expect(withoutTap.dx, 0); + }); + }); + + // --------------------------------------------------------------------------- + // Hidden badge must not catch taps + // --------------------------------------------------------------------------- + group('Hidden badge taps', () { + for (final animation in const [ + badges.BadgeAnimation.fade(), + badges.BadgeAnimation.rotation(), + badges.BadgeAnimation.fade(toAnimate: false), + ]) { + testWidgets( + 'taps reach the child and onTap is not called ' + '(${animation.animationType.name}, toAnimate: ${animation.toAnimate})', + (tester) async { + var childTaps = 0; + var badgeTaps = 0; + await tester.pumpWidget( + _wrapWithMaterialApp( + Center( + child: badges.Badge( + showBadge: false, + position: badges.BadgePosition.center(), + badgeAnimation: animation, + onTap: () => badgeTaps++, + badgeContent: const SizedBox(width: 40, height: 40), + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => childTaps++, + child: const SizedBox(width: 100, height: 100), + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + // Tap where the (invisible) badge actually is. + await tester.tap( + find.descendant( + of: find.byType(badges.Badge), + matching: find.byType(AnimatedContainer), + ), + ); + expect(childTaps, 1); + expect(badgeTaps, 0); + }, + ); + } + }); + + // --------------------------------------------------------------------------- + // Custom shapes with gradients + // --------------------------------------------------------------------------- + group('Custom shape gradients', () { + for (final shape in [ + badges.BadgeShape.twitter, + badges.BadgeShape.instagram, + ]) { + for (final gradient in const [ + badges.BadgeGradient.linear( + colors: [Colors.red, Colors.green, Colors.blue], + begin: AlignmentDirectional.topStart, + end: AlignmentDirectional.bottomEnd, + ), + badges.BadgeGradient.radial( + colors: [Colors.red, Colors.green, Colors.blue], + ), + badges.BadgeGradient.sweep( + colors: [Colors.red, Colors.green, Colors.blue], + stops: [0, 0.3, 1], + ), + ]) { + testWidgets('${shape.name} paints a 3-color ' + '${gradient.gradientType.name} gradient', (tester) async { + await tester.pumpWidget( + _wrapWithMaterialApp( + badges.Badge( + badgeStyle: badges.BadgeStyle( + shape: shape, + badgeGradient: gradient, + borderGradient: gradient, + borderSide: const BorderSide(width: 2), + ), + badgeContent: const Icon(Icons.check, size: 12), + ), + ), + ); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + }); + } + } + }); + + // --------------------------------------------------------------------------- + // Turning animations on at runtime + // --------------------------------------------------------------------------- + group('toAnimate false -> true', () { + for (final type in badges.BadgeAnimationType.values) { + testWidgets('badge stays visible (${type.name})', (tester) async { + Widget build(bool toAnimate) { + final animation = switch (type) { + badges.BadgeAnimationType.slide => badges.BadgeAnimation.slide( + toAnimate: toAnimate, + ), + badges.BadgeAnimationType.fade => badges.BadgeAnimation.fade( + toAnimate: toAnimate, + ), + badges.BadgeAnimationType.scale => badges.BadgeAnimation.scale( + toAnimate: toAnimate, + ), + badges.BadgeAnimationType.size => badges.BadgeAnimation.size( + toAnimate: toAnimate, + ), + badges.BadgeAnimationType.rotation => + badges.BadgeAnimation.rotation(toAnimate: toAnimate), + }; + return _wrapWithMaterialApp( + badges.Badge(badgeAnimation: animation, badgeContent: Text('1')), + ); + } + + await tester.pumpWidget(build(false)); + await tester.pumpWidget(build(true)); + await tester.pumpAndSettle(); + + final state = tester.state( + find.byType(badges.Badge), + ); + expect(state.animationController.value, 1); + expect(state.appearanceController.value, 1); + final opacity = tester.widget( + find.descendant( + of: find.byType(badges.Badge), + matching: find.byType(Opacity), + ), + ); + expect(opacity.opacity, 1); + }); + } + }); } Widget _wrapWithMaterialApp(Widget testWidget) { diff --git a/test/utils_tests.dart b/test/utils_tests.dart index 8f867a2..9d70d3f 100644 --- a/test/utils_tests.dart +++ b/test/utils_tests.dart @@ -54,9 +54,9 @@ void testUtils() { test('Passing null', () { final padding = CalculationUtils.calculatePadding(null); expect(padding.top, 8); - expect(padding.right, 10); + expect(padding.end, 10); expect(padding.bottom, 0); - expect(padding.left, 0); + expect(padding.start, 0); }); test('isCenter = true', () { @@ -64,8 +64,8 @@ void testUtils() { BadgePosition.custom(isCenter: true, top: -10, end: 20), ); expect(padding.top, 0); - expect(padding.right, 0); - expect(padding.left, 0); + expect(padding.end, 0); + expect(padding.start, 0); expect(padding.bottom, 0); }); @@ -74,9 +74,9 @@ void testUtils() { BadgePosition.custom(top: null, end: null, bottom: null, start: null), ); expect(padding.top, 0); - expect(padding.left, 0); + expect(padding.start, 0); expect(padding.bottom, 0); - expect(padding.right, 0); + expect(padding.end, 0); }); test('Top and start values', () { @@ -84,9 +84,9 @@ void testUtils() { BadgePosition.custom(top: -5, end: -5, bottom: -5, start: -5), ); expect(padding.top, 5); - expect(padding.left, 5); + expect(padding.start, 5); expect(padding.bottom, 0); - expect(padding.right, 0); + expect(padding.end, 0); }); test('Without top and start values and negative end bottom values', () { @@ -94,9 +94,9 @@ void testUtils() { BadgePosition.custom(end: -5, bottom: -5), ); expect(padding.top, 0); - expect(padding.left, 0); + expect(padding.start, 0); expect(padding.bottom, 5); - expect(padding.right, 5); + expect(padding.end, 5); }); test('Without top and start values and normal end bottom values', () { @@ -104,9 +104,9 @@ void testUtils() { BadgePosition.custom(end: 5, bottom: 5), ); expect(padding.top, 0); - expect(padding.left, 0); + expect(padding.start, 0); expect(padding.bottom, 0); - expect(padding.right, 0); + expect(padding.end, 0); }); }); @@ -174,7 +174,13 @@ void testUtils() { }); test('Custom alignment ', () { - final offset = getOffset(const Alignment(2, 2)); + final offset = getOffset(const Alignment(0.5, -0.5)); + expect(offset.dx, 75); + expect(offset.dy, 25); + }); + + test('Directional alignment resolves left-to-right', () { + final offset = getOffset(AlignmentDirectional.bottomEnd); expect(offset.dx, 100); expect(offset.dy, 100); });