diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 0000000..0e4b9f9 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,40 @@ +name: Pull Request Pipeline + +on: + pull_request: + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set Up Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.32.1' + channel: 'stable' + + - name: Check Formatting + run: flutter analyze + + test: + runs-on: ubuntu-latest + if: always() + needs: lint + + steps: + - uses: actions/checkout@v4 + + - name: Set Up Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.32.1' + channel: 'stable' + + - name: Get dependencies + run: flutter pub get + + - name: Run Tests + run: flutter test \ No newline at end of file diff --git a/.github/workflows/flutter.yml b/.github/workflows/push.yml similarity index 52% rename from .github/workflows/flutter.yml rename to .github/workflows/push.yml index 58064f3..d8d185b 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/push.yml @@ -1,38 +1,81 @@ -name: Flutter +name: Push Pipeline on: push: branches: - - "develop" - - "main" - pull_request: + - "develop" + - "main" jobs: - lint: - runs-on: macos-latest + generate_licenses: + runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Flutter + uses: subosito/flutter-action@v1.5.3 + with: + flutter-version: '3.32.1' + channel: "stable" + + - name: Get dependencies + run: flutter pub get + + - name: Generate oss_licenses.dart + run: flutter pub run flutter_oss_licenses:generate -o lib/presentation/views/about/licenses/oss_licenses.dart + + - name: Escape dollar signs in licenses + run: | + sed -i 's/\$/\\$/g' lib/presentation/views/about/licenses/oss_licenses.dart + + - name: Check for changes + id: check_changes + run: | + if [[ $(git status --porcelain) ]]; then + echo "changes_detected=true" >> $GITHUB_OUTPUT + else + echo "changes_detected=false" >> $GITHUB_OUTPUT + fi + + - name: Commit Changes + if: steps.check_changes.outputs.changes_detected == 'true' + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + git add . + git commit -m "Actions: Licenses updated [skip ci]" + git push + + lint: + runs-on: ubuntu-latest + needs: generate_licenses + if: always() + steps: + - name: Checkout code + uses: actions/checkout@v4 - name: Set Up Flutter uses: subosito/flutter-action@v2 with: - flutter-version: '3.29.2' + flutter-version: '3.32.1' channel: 'stable' - name: Check Formatting run: flutter analyze format: - runs-on: macos-latest + runs-on: ubuntu-latest needs: lint - if: ${{ failure() && needs.lint.result == 'failure' && github.event_name == 'push' && github.ref == 'refs/heads/develop'}} + if: ${{ failure() && needs.lint.result == 'failure'}} steps: - uses: actions/checkout@v4 - name: Set Up Flutter uses: subosito/flutter-action@v2 with: - flutter-version: '3.29.2' + flutter-version: '3.32.1' channel: 'stable' - name: Get & upgrade dependencies @@ -64,7 +107,7 @@ jobs: git push test: - runs-on: macos-latest + runs-on: ubuntu-latest if: always() needs: [lint, format] @@ -74,15 +117,18 @@ jobs: - name: Set Up Flutter uses: subosito/flutter-action@v2 with: - flutter-version: '3.29.2' + flutter-version: '3.32.1' channel: 'stable' + + - name: Get dependencies + run: flutter pub get - name: Run Tests run: flutter test build: - runs-on: macos-latest + runs-on: ubuntu-latest if: false # skips job needs: [lint, format, test] @@ -92,10 +138,10 @@ jobs: - name: Set Up Flutter uses: subosito/flutter-action@v2 with: - flutter-version: '3.29.2' + flutter-version: '3.32.1' channel: 'stable' - - name: Install dependencies + - name: Get dependencies run: flutter pub get - name: Analyze project source diff --git a/README.md b/README.md index e5e187d..44c0dcc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CABO Counter -![Version](https://img.shields.io/badge/Version-0.3.0-orange) +![Version](https://img.shields.io/badge/Version-0.5.8-orange) ![Flutter](https://img.shields.io/badge/Flutter-3.32.1-blue?logo=flutter) ![Dart](https://img.shields.io/badge/Dart-3.8.1-blue?logo=dart) ![iOS](https://img.shields.io/badge/iOS-18.5-white?logo=apple) @@ -12,25 +12,29 @@ A mobile score tracker for the card game Cabo, helping players effortlessly mana ## 📱 Description -Cabo Counter is an intuitive Flutter-based mobile application designed to enhance your CABO card game experience. It eliminates manual scorekeeping by automatically calculating points per round. +Cabo Counter is an intuitive Flutter-based mobile application designed to enhance your CABO card game experience. It eliminates manual scorekeeping by automatically calculating points per round. ## ✨ Features -- 🆕 Create new games with customizable rules - 👥 Support for 2-5 players - ⚖️ Two game modes: - - **100 Points Mode** (Standard) - - **Infinite Mode** (Casual play) + - **Point Limit Mode**: Play until a certain point limit is reached + - **Unlimited Mode**: Play without an limit and end the round at any point - 🔢 Automatic score calculation with: + - Falsly calling Cabo + - Exact 100-point bonus (score halving) - Kamikaze rule handling - - Exact 100-point bonus (score halving) -- 📊 Round history tracking +- 📊 Round history tracking via graph and table +- 🎨 Customizable + - Change the default settings for point limits and cabo penaltys + - Choose a default game mode for every new created game +- 💿 Im- and exporting certain games or the whole app data ## 🚀 Getting Started ### Prerequisites -- Flutter 3.24.5+ -- Dart 3.5.4+ +- Flutter 3.32.1+ +- Dart 3.8.1+ ### Installation @@ -43,18 +47,22 @@ flutter run ## 🎮 Usage -1. **Start New Game** -- Choose game mode (100 Points or Infinite) +1. **Start a new game** +- Click the "+"-Button +- Choose a game title and a game mode - Add 2-5 players 2. **Gameplay** -- Track rounds with automatic scoring -- Handle special rules (Kamikaze, exact 100 points) -- View real-time standings +- Open the first round +- Choose the player who called Cabo +- Enter the points of every player +- If given: Choose a Kamikaze player +- Navigate to the next round or back to the overview +- Let the app calculate all points for you -3. **Round Management** -- Automatic winner detection -- Penalty point calculation +3. **Statistics** +- View the progress graph for the game +- Get a detailed table overview for every points made or lost - Game-over detection (100 Points mode) ## 🃏 Key Rules Overview @@ -67,7 +75,8 @@ flutter run - Exact 100 points: Score halved ### Game End -- First player ≥101 points triggers final scoring +- First player ≥100 points triggers final scoring +- In unlimited mode you can end the game via the End Game Button - Lowest total score wins ## 🤝 Contributing diff --git a/analysis_options.yaml b/analysis_options.yaml index 2ce6b52..b3623a1 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -11,6 +11,3 @@ linter: prefer_const_literals_to_create_immutables: true unnecessary_const: true lines_longer_than_80_chars: false - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/assets/game-schema.json b/assets/game-schema.json new file mode 100644 index 0000000..9991c74 --- /dev/null +++ b/assets/game-schema.json @@ -0,0 +1,291 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Generated schema for a single cabo counter game", + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "gameTitle": { + "type": "string" + }, + "players": { + "type": "array", + "items": [ + { + "type": "string" + }, + { + "type": "string" + } + ] + }, + "pointLimit": { + "type": "integer" + }, + "caboPenalty": { + "type": "integer" + }, + "isPointsLimitEnabled": { + "type": "boolean" + }, + "isGameFinished": { + "type": "boolean" + }, + "winner": { + "type": "string" + }, + "roundNumber": { + "type": "integer" + }, + "playerScores": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + }, + "roundList": { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "roundNum": { + "type": "integer" + }, + "caboPlayerIndex": { + "type": "integer" + }, + "kamikazePlayerIndex": { + "type": "null" + }, + "scores": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + }, + "scoreUpdates": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + } + }, + "required": [ + "roundNum", + "caboPlayerIndex", + "kamikazePlayerIndex", + "scores", + "scoreUpdates" + ] + }, + { + "type": "object", + "properties": { + "roundNum": { + "type": "integer" + }, + "caboPlayerIndex": { + "type": "integer" + }, + "kamikazePlayerIndex": { + "type": "null" + }, + "scores": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + }, + "scoreUpdates": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + } + }, + "required": [ + "roundNum", + "caboPlayerIndex", + "kamikazePlayerIndex", + "scores", + "scoreUpdates" + ] + }, + { + "type": "object", + "properties": { + "roundNum": { + "type": "integer" + }, + "caboPlayerIndex": { + "type": "integer" + }, + "kamikazePlayerIndex": { + "type": "null" + }, + "scores": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + }, + "scoreUpdates": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + } + }, + "required": [ + "roundNum", + "caboPlayerIndex", + "kamikazePlayerIndex", + "scores", + "scoreUpdates" + ] + }, + { + "type": "object", + "properties": { + "roundNum": { + "type": "integer" + }, + "caboPlayerIndex": { + "type": "integer" + }, + "kamikazePlayerIndex": { + "type": "null" + }, + "scores": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + }, + "scoreUpdates": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + } + }, + "required": [ + "roundNum", + "caboPlayerIndex", + "kamikazePlayerIndex", + "scores", + "scoreUpdates" + ] + }, + { + "type": "object", + "properties": { + "roundNum": { + "type": "integer" + }, + "caboPlayerIndex": { + "type": "integer" + }, + "kamikazePlayerIndex": { + "type": "null" + }, + "scores": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + }, + "scoreUpdates": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ] + } + }, + "required": [ + "roundNum", + "caboPlayerIndex", + "kamikazePlayerIndex", + "scores", + "scoreUpdates" + ] + } + ] + } + }, + "required": [ + "id", + "createdAt", + "gameTitle", + "players", + "pointLimit", + "caboPenalty", + "isPointsLimitEnabled", + "isGameFinished", + "winner", + "roundNumber", + "playerScores", + "roundList" + ] +} + diff --git a/assets/schema.json b/assets/game_list-schema.json similarity index 94% rename from assets/schema.json rename to assets/game_list-schema.json index 17d7faa..26e4278 100644 --- a/assets/schema.json +++ b/assets/game_list-schema.json @@ -1,10 +1,13 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Generated schema for cabo game data", + "title": "Generated schema for the cabo counter game data", "type": "array", "items": { "type": "object", "properties": { + "id": { + "type": "string" + }, "createdAt": { "type": "string" }, diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/l10n.yaml b/l10n.yaml index 239fdc6..f69305d 100644 --- a/l10n.yaml +++ b/l10n.yaml @@ -1,5 +1,6 @@ -arb-dir: lib/l10n +arb-dir: lib/l10n/arb template-arb-file: app_de.arb -untranslated-messages-file: lib/l10n/untranslated_messages.json +untranslated-messages-file: lib/l10n/arb/untranslated_messages.json nullable-getter: false -output-localization-file: app_localizations.dart \ No newline at end of file +output-localization-file: app_localizations.dart +output-dir: lib/l10n/generated \ No newline at end of file diff --git a/lib/core/constants.dart b/lib/core/constants.dart new file mode 100644 index 0000000..c7f7589 --- /dev/null +++ b/lib/core/constants.dart @@ -0,0 +1,57 @@ +import 'package:rate_my_app/rate_my_app.dart'; + +/// A utility class that holds constant values and configuration settings +/// used throughout the application, such as external links, email addresses, +/// and timing parameters for UI elements. +/// +/// This class also provides an instance of [RateMyApp] for managing +/// in-app rating prompts. +class Constants { + /// Indicates the current development phase of the app + static const String appDevPhase = 'Beta'; + + /// Links to various social media profiles and resources related to the app. + /// URL to my Instagram profile + static const String kInstagramLink = 'https://instagram.felixkirchner.de'; + + /// URL to my GitHub profile + static const String kGithubLink = 'https://github.felixkirchner.de'; + + /// URL to the GitHub issues page for reporting bugs or requesting features. + static const String kGithubIssuesLink = + 'https://cabo-counter-issues.felixkirchner.de'; + + /// URL to the GitHub wiki for additional documentation and guides. + static const String kGithubWikiLink = + 'https://cabo-counter-wiki.felixkirchner.de'; + + /// Official email address for user inquiries and support. + static const String kEmail = 'cabocounter@felixkirchner.de'; + + /// URL to the app's privacy policy page. + static const String kPrivacyPolicyLink = + 'https://cabo-counter-datenschutz.felixkirchner.de'; + + /// URL to the app's imprint page, containing legal information. + static const String kLegalLink = 'https://impressum.felixkirchner.de'; + + /// Instance of [RateMyApp] configured to prompt users for app store ratings. + static RateMyApp rateMyApp = RateMyApp( + appStoreIdentifier: '6747105718', + minDays: 15, + remindDays: 45, + minLaunches: 15, + remindLaunches: 40); + + /// Delay in milliseconds before a pop-up appears. + static const int kPopUpDelay = 300; + + /// Delay in milliseconds before the round view appears after the previous one is closed. + static const int kRoundViewDelay = 600; + + /// Duration in milliseconds for the fade-in animation of texts. + static const int kFadeInDuration = 300; + + /// Duration in milliseconds for the keyboard to fully disappear. + static const int kKeyboardDelay = 300; +} diff --git a/lib/core/custom_theme.dart b/lib/core/custom_theme.dart new file mode 100644 index 0000000..62d0afb --- /dev/null +++ b/lib/core/custom_theme.dart @@ -0,0 +1,69 @@ +import 'package:flutter/cupertino.dart'; + +class CustomTheme { + /// Main Theme of the App + /// Primary white color mainly used for text + static Color white = CupertinoColors.white; + + /// Red color, typically used for destructive actions or error states + static Color red = CupertinoColors.destructiveRed; + + /// Primary color of the app, used for buttons, highlights, and interactive elements + static Color primaryColor = CupertinoColors.systemGreen; + + /// Background color for the main app scaffold and views + static Color backgroundColor = const Color(0xFF101010); + + /// Background color for main UI elements like cards or containers. + static Color mainElementBackgroundColor = CupertinoColors.darkBackgroundGray; + + /// Background color for player tiles in lists. + static Color playerTileColor = CupertinoColors.secondaryLabel; + + /// Background color for buttons and interactive controls. + static Color buttonBackgroundColor = const Color(0xFF202020); + + /// Color used to highlight the kamikaze button and players + static Color kamikazeColor = CupertinoColors.systemYellow; + + // Line Colors for GraphView + static const Color graphColor1 = Color(0xFFF44336); + static const Color graphColor2 = Color(0xFF2196F3); + static const Color graphColor3 = Color(0xFFFFA726); + static const Color graphColor4 = Color(0xFF9C27B0); + static final Color graphColor5 = primaryColor; + + // Colors for PointsView + /// Color used to indicate a loss of points in the UI. + static Color pointLossColor = primaryColor; + + /// Color used to indicate a gain of points in the UI. + static const Color pointGainColor = Color(0xFFF44336); + + // Text Styles + /// Text style for mode titles, typically used in headers or section titles. + static TextStyle modeTitle = TextStyle( + color: primaryColor, + fontSize: 20, + fontWeight: FontWeight.bold, + ); + + /// Default text style for mode descriptions. + static const TextStyle modeDescription = TextStyle( + fontSize: 16, + ); + + /// Text style for titles of sections of [CupertinoListTile]. + static TextStyle rowTitle = TextStyle( + fontSize: 20, + color: primaryColor, + fontWeight: FontWeight.bold, + ); + + /// Text style for round titles, used for prominent display of the round title + static TextStyle roundTitle = TextStyle( + fontSize: 60, + color: white, + fontWeight: FontWeight.bold, + ); +} diff --git a/lib/data/models/game_manager.dart b/lib/data/models/game_manager.dart index 074aff4..6ec54c6 100644 --- a/lib/data/models/game_manager.dart +++ b/lib/data/models/game_manager.dart @@ -1,5 +1,6 @@ import 'package:cabo_counter/data/models/game_session.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; class GameManager extends ChangeNotifier { @@ -10,32 +11,68 @@ class GameManager extends ChangeNotifier { /// sorts the list in descending order based on the creation date, and notifies listeners of the change. /// It also saves the updated game sessions to local storage. /// Returns the index of the newly added session in the sorted list. - Future addGameSession(GameSession session) async { + int addGameSession(GameSession session) { session.addListener(() { notifyListeners(); // Propagate session changes }); gameList.add(session); - print( - '[game_manager.dart] Added game session: ${session.gameTitle} at ${session.createdAt}'); gameList.sort((a, b) => b.createdAt.compareTo(a.createdAt)); - print( - '[game_manager.dart] Sorted game sessions by creation date. Total sessions: ${gameList.length}'); notifyListeners(); - await LocalStorageService.saveGameSessions(); - print('[game_manager.dart] Saved game sessions to local storage.'); + LocalStorageService.saveGameSessions(); return gameList.indexOf(session); } + /// Retrieves a game session by its id. + /// Takes a String [id] as input. It searches the `gameList` for a session + /// with a matching id and returns it if found. + /// If no session is found, it returns null. + GameSession? getGameSessionById(String id) { + return gameList.firstWhereOrNull((session) => session.id == id); + } + /// Removes a game session from the list and sorts it by creation date. /// Takes a [index] as input. It then removes the session at the specified index from the `gameList`, /// sorts the list in descending order based on the creation date, and notifies listeners of the change. /// It also saves the updated game sessions to local storage. - void removeGameSession(int index) { + void removeGameSessionByIndex(int index) { gameList[index].removeListener(notifyListeners); gameList.removeAt(index); notifyListeners(); LocalStorageService.saveGameSessions(); } + + /// Removes a game session by its ID. + /// Takes a String [id] as input. It finds the index of the game session with the matching ID + /// in the `gameList`, and then calls `removeGameSessionByIndex` with that index. + void removeGameSessionById(String id) { + final int index = + gameList.indexWhere((session) => session.id.toString() == id); + if (index == -1) return; + removeGameSessionByIndex(index); + } + + /// Retrieves a game session by its ID. + /// Takes a String [id] as input. It finds the game session with the matching id + bool gameExistsInGameList(String id) { + return gameList.any((session) => session.id.toString() == id); + } + + /// Ends a game session if its in unlimited mode. + /// Takes a String [id] as input. It finds the index of the game + /// session with the matching ID marks it as finished, + void endGame(String id) { + final int index = + gameList.indexWhere((session) => session.id.toString() == id); + + // Game session not found or not in unlimited mode + if (index == -1 || gameList[index].isPointsLimitEnabled == true) return; + + gameList[index].roundNumber--; + gameList[index].isGameFinished = true; + gameList[index].setWinner(); + notifyListeners(); + LocalStorageService.saveGameSessions(); + } } final gameManager = GameManager(); diff --git a/lib/data/models/game_session.dart b/lib/data/models/game_session.dart index 444f962..2c0f601 100644 --- a/lib/data/models/game_session.dart +++ b/lib/data/models/game_session.dart @@ -1,5 +1,6 @@ import 'package:cabo_counter/data/models/round.dart'; import 'package:flutter/cupertino.dart'; +import 'package:uuid/uuid.dart'; /// This class represents a game session for Cabo game. /// [createdAt] is the timestamp of when the game session was created. @@ -12,6 +13,7 @@ import 'package:flutter/cupertino.dart'; /// [isGameFinished] is a boolean indicating if the game has ended yet. /// [winner] is the name of the player who won the game. class GameSession extends ChangeNotifier { + final String id; final DateTime createdAt; final String gameTitle; final List players; @@ -25,6 +27,7 @@ class GameSession extends ChangeNotifier { List roundList = []; GameSession({ + required this.id, required this.createdAt, required this.gameTitle, required this.players, @@ -37,13 +40,14 @@ class GameSession extends ChangeNotifier { @override toString() { - return ('GameSession: [createdAt: $createdAt, gameTitle: $gameTitle, ' + return ('GameSession: [id: $id, createdAt: $createdAt, gameTitle: $gameTitle, ' 'isPointsLimitEnabled: $isPointsLimitEnabled, pointLimit: $pointLimit, caboPenalty: $caboPenalty,' ' players: $players, playerScores: $playerScores, roundList: $roundList, winner: $winner]'); } /// Converts the GameSession object to a JSON map. Map toJson() => { + 'id': id, 'createdAt': createdAt.toIso8601String(), 'gameTitle': gameTitle, 'players': players, @@ -59,7 +63,8 @@ class GameSession extends ChangeNotifier { /// Creates a GameSession object from a JSON map. GameSession.fromJson(Map json) - : createdAt = DateTime.parse(json['createdAt']), + : id = json['id'] ?? const Uuid().v1(), + createdAt = DateTime.parse(json['createdAt']), gameTitle = json['gameTitle'], players = List.from(json['players']), pointLimit = json['pointLimit'], @@ -72,15 +77,6 @@ class GameSession extends ChangeNotifier { roundList = (json['roundList'] as List).map((e) => Round.fromJson(e)).toList(); - /// Returns the length of all player names combined. - int getLengthOfPlayerNames() { - int length = 0; - for (String player in players) { - length += player.length; - } - return length; - } - /// Assigns 50 points to all players except the kamikaze player. /// [kamikazePlayerIndex] is the index of the kamikaze player. void applyKamikaze(int roundNum, int kamikazePlayerIndex) { @@ -227,7 +223,7 @@ class GameSession extends ChangeNotifier { /// This method updates the points of each player after a round. /// It first uses the _sumPoints() method to calculate the total points of each player. - /// Then, it checks if any player has reached 100 points. If so, it marks + /// Then, it checks if any player has reached 100 points. If so, saves their indices and marks /// that player as having reached 100 points in that corresponding [Round] object. /// If the game has the point limit activated, it first applies the /// _subtractPointsForReachingHundred() method to subtract 50 points @@ -235,21 +231,31 @@ class GameSession extends ChangeNotifier { /// It then checks if any player has exceeded 100 points. If so, it sets /// isGameFinished to true and calls the _setWinner() method to determine /// the winner. - Future updatePoints() async { + /// It returns a list of players indices who reached 100 points (bonus player) + /// in the current round for the [RoundView] to show a popup + List updatePoints() { + List bonusPlayers = []; _sumPoints(); + if (isPointsLimitEnabled) { - _checkHundredPointsReached(); + bonusPlayers = _checkHundredPointsReached(); + bool limitExceeded = false; for (int i = 0; i < playerScores.length; i++) { if (playerScores[i] > pointLimit) { isGameFinished = true; + limitExceeded = true; print('${players[i]} hat die 100 Punkte ueberschritten, ' 'deswegen wurde das Spiel beendet'); - _setWinner(); + setWinner(); } } + if (!limitExceeded) { + isGameFinished = false; + } } notifyListeners(); + return bonusPlayers; } @visibleForTesting @@ -270,30 +276,37 @@ class GameSession extends ChangeNotifier { /// Checks if a player has reached 100 points in the current round. /// If so, it updates the [scoreUpdate] List by subtracting 50 points from /// the corresponding round update. - void _checkHundredPointsReached() { + List _checkHundredPointsReached() { + List bonusPlayers = []; for (int i = 0; i < players.length; i++) { if (playerScores[i] == pointLimit) { + bonusPlayers.add(i); print('${players[i]} hat genau 100 Punkte erreicht und bekommt ' - 'deswegen 50 Punkte abgezogen'); - roundList[roundNumber - 1].scoreUpdates[i] -= 50; + 'deswegen ${(pointLimit / 2).round()} Punkte abgezogen'); + roundList[roundNumber - 1].scoreUpdates[i] -= (pointLimit / 2).round(); } } _sumPoints(); + return bonusPlayers; } /// Determines the winner of the game session. /// It iterates through the player scores and finds the player /// with the lowest score. - void _setWinner() { - int score = playerScores[0]; - String lowestPlayer = players[0]; + void setWinner() { + int minScore = playerScores.reduce((a, b) => a < b ? a : b); + List lowestPlayers = []; for (int i = 0; i < players.length; i++) { - if (playerScores[i] < score) { - score = playerScores[i]; - lowestPlayer = players[i]; + if (playerScores[i] == minScore) { + lowestPlayers.add(players[i]); } } - winner = lowestPlayer; + if (lowestPlayers.length > 1) { + winner = + '${lowestPlayers.sublist(0, lowestPlayers.length - 1).join(', ')} & ${lowestPlayers.last}'; + } else { + winner = lowestPlayers.first; + } notifyListeners(); } diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb deleted file mode 100644 index 3cb8c5e..0000000 --- a/lib/l10n/app_de.arb +++ /dev/null @@ -1,93 +0,0 @@ -{ - "@@locale": "de", - - "app_name": "Cabo Counter", - "round": "Runde", - "rounds": "Runden", - "mode": "Modus", - "points": "Punkte", - "unlimited": "Unbegrenzt", - "delete": "Löschen", - "cancel": "Abbrechen", - "game": "Spiel", - "ok": "OK", - "player": "Spieler:in", - "players": "Spieler:innen", - "name": "Name", - "back": "Zurück", - - "home": "Home", - "about": "Über", - - "empty_text_1": "Ganz schön leer hier...", - "empty_text_2": "Füge über den Button oben rechts eine neue Runde hinzu", - "delete_game_title": "Spiel löschen?", - "delete_game_message": "Bist du sicher, dass du die Runde {gameTitle} löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.", - "@delete_game_message": { - "placeholders": { - "gameTitle": { - "type": "String" - } - } - }, - "overview": "Übersicht", - "new_game": "Neues Spiel", - "game_title": "Titel des Spiels", - "select_mode": "Wähle einen Modus", - "add_player": "Spieler:in hinzufügen", - "create_game": "Spiel erstellen", - "max_players_title": "Maximale Anzahl erreicht", - "max_players_message": "Es können maximal 5 Spieler:innen hinzugefügt werden.", - "no_gameTitle_title": "Kein Titel", - "no_gameTitle_message": "Es muss ein Titel für das Spiel eingegeben werden.", - "no_mode_title": "Kein Modus", - "no_mode_message": "Es muss ein Spielmodus ausgewählt werden.", - "min_players_title": "Zu wenig Spieler:innen", - "min_players_message": "Es müssen mindestens 2 Spieler:innen hinzugefügt werden", - "no_name_title": "Kein Name", - "no_name_message": "Jeder Spieler muss einen Namen haben.", - - "select_game_mode": "Spielmodus auswählen", - "point_limit_description": "Es wird so lange gespielt, bis ein:e Spieler:in mehr als {pointLimit} Punkte erreicht", - "@point_limit_description": { - "placeholders": { - "pointLimit": { - "type": "int" - } - } - }, - "unlimited_description": "Dem Spiel sind keine Grenzen gesetzt. Es wird so lange gespielt, bis ihr keine Lust mehr habt.", - - "results": "Ergebnisse", - "who_said_cabo": "Wer hat CABO gesagt?", - "kamikaze": "Kamikaze", - "done": "Fertig", - "next_round": "Nächste Runde", - - "statistics": "Statistiken", - "delete_game": "Spiel löschen", - "new_game_same_settings": "Neues Spiel mit gleichen Einstellungen", - "export_game": "Spiel exportieren", - - "game_process": "Spielverlauf", - - "settings": "Einstellungen", - "cabo_penalty": "Cabo-Strafe", - "cabo_penalty_subtitle": "... für falsches Cabo sagen", - "point_limit": "Punkte-Limit", - "point_limit_subtitle": "... hier ist Schluss", - "reset_to_default": "Auf Standard zurücksetzen", - "game_data": "Spieldaten", - "import_data": "Daten importieren", - "export_data": "Daten exportieren", - "error": "Fehler", - "error_import": "Datei konnte nicht importiert werden", - "error_export": "Datei konnte nicht exportiert werden", - "error_found": "Fehler gefunden?", - "create_issue": "Issue erstellen", - "app_version": "App-Version", - "build": "Build", - "load_version": "Lade Version...", - - "about_text": "Hey :) Danke, dass du als eine:r der ersten User meiner ersten eigenen App dabei bist! Ich hab sehr viel Arbeit in dieses Projekt gesteckt und auch, wenn ich (hoffentlich) an vieles Gedacht hab, wird auf jeden Fall noch nicht alles 100% funktionieren. Solltest du also irgendwelche Fehler entdecken oder Feedback zum Design oder der Benutzerfreundlichkeit haben, teile Sie mir gern über die Testflight App oder auf den dir bekannten Wegen mit. Danke! " -} \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb deleted file mode 100644 index f76e04a..0000000 --- a/lib/l10n/app_en.arb +++ /dev/null @@ -1,93 +0,0 @@ -{ - "@@locale": "en", - - "app_name": "Cabo Counter", - "round": "Round", - "rounds": "Rounds", - "mode": "Mode", - "points": "Points", - "unlimited": "Unlimited", - "delete": "Delete", - "cancel": "Cancel", - "game": "Game", - "ok": "OK", - "player": "Player", - "players": "Players", - "name": "Name", - "back": "Back", - - "home": "Home", - "about": "About", - - "empty_text_1": "Pretty empty here...", - "empty_text_2": "Add a new round using the button in the top right corner.", - "delete_game_title": "Delete game?", - "delete_game_message": "Are you sure you want to delete the game {gameTitle}? This action cannot be undone.", - "@delete_game_message": { - "placeholders": { - "gameTitle": { - "type": "String" - } - } - }, - "overview": "Overview", - "new_game": "New Game", - "game_title": "Game Title", - "select_mode": "Select a mode", - "add_player": "Add Player", - "create_game": "Create Game", - "max_players_title": "Maximum reached", - "max_players_message": "A maximum of 5 players can be added.", - "no_gameTitle_title": "No Title", - "no_gameTitle_message": "You must enter a title for the game.", - "no_mode_title": "No Mode", - "no_mode_message": "You must select a game mode.", - "min_players_title": "Too few players", - "min_players_message": "At least 2 players must be added.", - "no_name_title": "No Name", - "no_name_message": "Each player must have a name.", - - "select_game_mode": "Select game mode", - "point_limit_description": "The game ends when a player reaches more than {pointLimit} points.", - "@point_limit_description": { - "placeholders": { - "pointLimit": { - "type": "int" - } - } - }, - "unlimited_description": "There is no limit. The game continues until you decide to stop.", - - "results": "Results", - "who_said_cabo": "Who said CABO?", - "kamikaze": "Kamikaze", - "done": "Done", - "next_round": "Next Round", - - "statistics": "Statistics", - "delete_game": "Delete Game", - "new_game_same_settings": "New Game with same Settings", - "export_game": "Export Game", - - "game_process": "Spielverlauf", - - "settings": "Settings", - "cabo_penalty": "Cabo Penalty", - "cabo_penalty_subtitle": "... for falsely calling Cabo.", - "point_limit": "Point Limit", - "point_limit_subtitle": "... the game ends here.", - "reset_to_default": "Reset to Default", - "game_data": "Game Data", - "import_data": "Import Data", - "export_data": "Export Data", - "error": "Error", - "error_import": "Could not import file", - "error_export": "Could not export file", - "error_found": "Found a bug?", - "create_issue": "Create Issue", - "app_version": "App Version", - "load_version": "Loading version...", - "build": "Build", - - "about_text": "Hey :) Thanks for being one of the first users of my app! I’ve put a lot of work into this project, and even though I tried to think of everything, it might not work perfectly just yet. So if you discover any bugs or have feedback on the design or usability, please let me know via the TestFlight app or by sending me a message or email. Thank you very much!" -} diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart deleted file mode 100644 index 434b5f1..0000000 --- a/lib/l10n/app_localizations_de.dart +++ /dev/null @@ -1,221 +0,0 @@ -// ignore: unused_import -import 'package:intl/intl.dart' as intl; -import 'app_localizations.dart'; - -// ignore_for_file: type=lint - -/// The translations for German (`de`). -class AppLocalizationsDe extends AppLocalizations { - AppLocalizationsDe([String locale = 'de']) : super(locale); - - @override - String get app_name => 'Cabo Counter'; - - @override - String get round => 'Runde'; - - @override - String get rounds => 'Runden'; - - @override - String get mode => 'Modus'; - - @override - String get points => 'Punkte'; - - @override - String get unlimited => 'Unbegrenzt'; - - @override - String get delete => 'Löschen'; - - @override - String get cancel => 'Abbrechen'; - - @override - String get game => 'Spiel'; - - @override - String get ok => 'OK'; - - @override - String get player => 'Spieler:in'; - - @override - String get players => 'Spieler:innen'; - - @override - String get name => 'Name'; - - @override - String get back => 'Zurück'; - - @override - String get home => 'Home'; - - @override - String get about => 'Über'; - - @override - String get empty_text_1 => 'Ganz schön leer hier...'; - - @override - String get empty_text_2 => - 'Füge über den Button oben rechts eine neue Runde hinzu'; - - @override - String get delete_game_title => 'Spiel löschen?'; - - @override - String delete_game_message(String gameTitle) { - return 'Bist du sicher, dass du die Runde $gameTitle löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'; - } - - @override - String get overview => 'Übersicht'; - - @override - String get new_game => 'Neues Spiel'; - - @override - String get game_title => 'Titel des Spiels'; - - @override - String get select_mode => 'Wähle einen Modus'; - - @override - String get add_player => 'Spieler:in hinzufügen'; - - @override - String get create_game => 'Spiel erstellen'; - - @override - String get max_players_title => 'Maximale Anzahl erreicht'; - - @override - String get max_players_message => - 'Es können maximal 5 Spieler:innen hinzugefügt werden.'; - - @override - String get no_gameTitle_title => 'Kein Titel'; - - @override - String get no_gameTitle_message => - 'Es muss ein Titel für das Spiel eingegeben werden.'; - - @override - String get no_mode_title => 'Kein Modus'; - - @override - String get no_mode_message => 'Es muss ein Spielmodus ausgewählt werden.'; - - @override - String get min_players_title => 'Zu wenig Spieler:innen'; - - @override - String get min_players_message => - 'Es müssen mindestens 2 Spieler:innen hinzugefügt werden'; - - @override - String get no_name_title => 'Kein Name'; - - @override - String get no_name_message => 'Jeder Spieler muss einen Namen haben.'; - - @override - String get select_game_mode => 'Spielmodus auswählen'; - - @override - String point_limit_description(int pointLimit) { - return 'Es wird so lange gespielt, bis ein:e Spieler:in mehr als $pointLimit Punkte erreicht'; - } - - @override - String get unlimited_description => - 'Dem Spiel sind keine Grenzen gesetzt. Es wird so lange gespielt, bis ihr keine Lust mehr habt.'; - - @override - String get results => 'Ergebnisse'; - - @override - String get who_said_cabo => 'Wer hat CABO gesagt?'; - - @override - String get kamikaze => 'Kamikaze'; - - @override - String get done => 'Fertig'; - - @override - String get next_round => 'Nächste Runde'; - - @override - String get statistics => 'Statistiken'; - - @override - String get delete_game => 'Spiel löschen'; - - @override - String get new_game_same_settings => 'Neues Spiel mit gleichen Einstellungen'; - - @override - String get export_game => 'Spiel exportieren'; - - @override - String get game_process => 'Spielverlauf'; - - @override - String get settings => 'Einstellungen'; - - @override - String get cabo_penalty => 'Cabo-Strafe'; - - @override - String get cabo_penalty_subtitle => '... für falsches Cabo sagen'; - - @override - String get point_limit => 'Punkte-Limit'; - - @override - String get point_limit_subtitle => '... hier ist Schluss'; - - @override - String get reset_to_default => 'Auf Standard zurücksetzen'; - - @override - String get game_data => 'Spieldaten'; - - @override - String get import_data => 'Daten importieren'; - - @override - String get export_data => 'Daten exportieren'; - - @override - String get error => 'Fehler'; - - @override - String get error_import => 'Datei konnte nicht importiert werden'; - - @override - String get error_export => 'Datei konnte nicht exportiert werden'; - - @override - String get error_found => 'Fehler gefunden?'; - - @override - String get create_issue => 'Issue erstellen'; - - @override - String get app_version => 'App-Version'; - - @override - String get build => 'Build'; - - @override - String get load_version => 'Lade Version...'; - - @override - String get about_text => - 'Hey :) Danke, dass du als eine:r der ersten User meiner ersten eigenen App dabei bist! Ich hab sehr viel Arbeit in dieses Projekt gesteckt und auch, wenn ich (hoffentlich) an vieles Gedacht hab, wird auf jeden Fall noch nicht alles 100% funktionieren. Solltest du also irgendwelche Fehler entdecken oder Feedback zum Design oder der Benutzerfreundlichkeit haben, teile Sie mir gern über die Testflight App oder auf den dir bekannten Wegen mit. Danke! '; -} diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart deleted file mode 100644 index 64cdb77..0000000 --- a/lib/l10n/app_localizations_en.dart +++ /dev/null @@ -1,218 +0,0 @@ -// ignore: unused_import -import 'package:intl/intl.dart' as intl; -import 'app_localizations.dart'; - -// ignore_for_file: type=lint - -/// The translations for English (`en`). -class AppLocalizationsEn extends AppLocalizations { - AppLocalizationsEn([String locale = 'en']) : super(locale); - - @override - String get app_name => 'Cabo Counter'; - - @override - String get round => 'Round'; - - @override - String get rounds => 'Rounds'; - - @override - String get mode => 'Mode'; - - @override - String get points => 'Points'; - - @override - String get unlimited => 'Unlimited'; - - @override - String get delete => 'Delete'; - - @override - String get cancel => 'Cancel'; - - @override - String get game => 'Game'; - - @override - String get ok => 'OK'; - - @override - String get player => 'Player'; - - @override - String get players => 'Players'; - - @override - String get name => 'Name'; - - @override - String get back => 'Back'; - - @override - String get home => 'Home'; - - @override - String get about => 'About'; - - @override - String get empty_text_1 => 'Pretty empty here...'; - - @override - String get empty_text_2 => - 'Add a new round using the button in the top right corner.'; - - @override - String get delete_game_title => 'Delete game?'; - - @override - String delete_game_message(String gameTitle) { - return 'Are you sure you want to delete the game $gameTitle? This action cannot be undone.'; - } - - @override - String get overview => 'Overview'; - - @override - String get new_game => 'New Game'; - - @override - String get game_title => 'Game Title'; - - @override - String get select_mode => 'Select a mode'; - - @override - String get add_player => 'Add Player'; - - @override - String get create_game => 'Create Game'; - - @override - String get max_players_title => 'Maximum reached'; - - @override - String get max_players_message => 'A maximum of 5 players can be added.'; - - @override - String get no_gameTitle_title => 'No Title'; - - @override - String get no_gameTitle_message => 'You must enter a title for the game.'; - - @override - String get no_mode_title => 'No Mode'; - - @override - String get no_mode_message => 'You must select a game mode.'; - - @override - String get min_players_title => 'Too few players'; - - @override - String get min_players_message => 'At least 2 players must be added.'; - - @override - String get no_name_title => 'No Name'; - - @override - String get no_name_message => 'Each player must have a name.'; - - @override - String get select_game_mode => 'Select game mode'; - - @override - String point_limit_description(int pointLimit) { - return 'The game ends when a player reaches more than $pointLimit points.'; - } - - @override - String get unlimited_description => - 'There is no limit. The game continues until you decide to stop.'; - - @override - String get results => 'Results'; - - @override - String get who_said_cabo => 'Who said CABO?'; - - @override - String get kamikaze => 'Kamikaze'; - - @override - String get done => 'Done'; - - @override - String get next_round => 'Next Round'; - - @override - String get statistics => 'Statistics'; - - @override - String get delete_game => 'Delete Game'; - - @override - String get new_game_same_settings => 'New Game with same Settings'; - - @override - String get export_game => 'Export Game'; - - @override - String get game_process => 'Spielverlauf'; - - @override - String get settings => 'Settings'; - - @override - String get cabo_penalty => 'Cabo Penalty'; - - @override - String get cabo_penalty_subtitle => '... for falsely calling Cabo.'; - - @override - String get point_limit => 'Point Limit'; - - @override - String get point_limit_subtitle => '... the game ends here.'; - - @override - String get reset_to_default => 'Reset to Default'; - - @override - String get game_data => 'Game Data'; - - @override - String get import_data => 'Import Data'; - - @override - String get export_data => 'Export Data'; - - @override - String get error => 'Error'; - - @override - String get error_import => 'Could not import file'; - - @override - String get error_export => 'Could not export file'; - - @override - String get error_found => 'Found a bug?'; - - @override - String get create_issue => 'Create Issue'; - - @override - String get app_version => 'App Version'; - - @override - String get build => 'Build'; - - @override - String get load_version => 'Loading version...'; - - @override - String get about_text => - 'Hey :) Thanks for being one of the first users of my app! I’ve put a lot of work into this project, and even though I tried to think of everything, it might not work perfectly just yet. So if you discover any bugs or have feedback on the design or usability, please let me know via the TestFlight app or by sending me a message or email. Thank you very much!'; -} diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb new file mode 100644 index 0000000..86ef939 --- /dev/null +++ b/lib/l10n/arb/app_de.arb @@ -0,0 +1,167 @@ +{ + "@@locale": "de", + + "app_name": "Cabo Counter", + "round": "Runde", + "rounds": "Runden", + "mode": "Modus", + "points": "Punkte", + "unlimited": "Unbegrenzt", + "delete": "Löschen", + "cancel": "Abbrechen", + "game": "Spiel", + "games": "Spiele", + "gamemode": "Spielmodus", + "ok": "OK", + "player": "Spieler:in", + "players": "Spieler:innen", + "name": "Name", + "back": "Zurück", + + "home": "Home", + + "about": "Über", + "licenses": "Lizenzen", + "license_details": "Lizenzdetails", + "no_license_text": "Keine Lizenz verfügbar", + "legal_notice": "Impressum", + + "empty_text_1": "Ganz schön leer hier...", + "empty_text_2": "Füge über den Button oben rechts eine neue Runde hinzu", + "delete_game_title": "Spiel löschen?", + "delete_game_message": "Bist du sicher, dass du das Spiel \"{gameTitle}\" löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.", + "@delete_game_message": { + "placeholders": { + "gameTitle": { + "type": "String" + } + } + }, + "pre_rating_title": "Gefällt dir die App?", + "pre_rating_message": "Feedback hilft mir, die App zu verbessern. Vielen Dank!", + "yes": "Ja", + "no": "Nein", + "bad_rating_title": "Unzufrieden mit der App?", + "bad_rating_message": "Schreib mir gerne direkt eine E-Mail, damit wir dein Problem lösen können!", + "contact_email": "E-Mail schreiben", + "email_subject": "Feedback: Cabo Counter App", + "email_body": "Ich habe folgendes Feedback...", + + "overview": "Übersicht", + "new_game": "Neues Spiel", + "game_title": "Titel des Spiels", + "select_mode": "Wähle einen Modus", + "add_player": "Spieler:in hinzufügen", + "create_game": "Spiel erstellen", + "max_players_title": "Maximale Anzahl erreicht", + "max_players_message": "Es können maximal 5 Spieler:innen hinzugefügt werden.", + "no_gameTitle_title": "Kein Titel", + "no_gameTitle_message": "Es muss ein Titel für das Spiel eingegeben werden.", + "no_mode_title": "Kein Modus", + "no_mode_message": "Es muss ein Spielmodus ausgewählt werden.", + "min_players_title": "Zu wenig Spieler:innen", + "min_players_message": "Es müssen mindestens 2 Spieler:innen hinzugefügt werden", + "no_name_title": "Kein Name", + "no_name_message": "Jede:r Spieler:in muss einen Namen haben.", + + "select_game_mode": "Spielmodus auswählen", + "no_mode_selected": "Wähle einen Spielmodus", + "no_default_mode": "Kein Modus", + "no_default_description": "Entscheide bei jedem Spiel selber, welchen Modus du spielen möchtest.", + "point_limit_description": "Es wird so lange gespielt, bis ein:e Spieler:in mehr als {pointLimit} Punkte erreicht", + "@point_limit_description": { + "placeholders": { + "pointLimit": { + "type": "int" + } + } + }, + "unlimited_description": "Es wird so lange gespielt, bis ihr keine Lust mehr habt. Das Spiel kann jederzeit manuell beendet werden.", + + "results": "Ergebnisse", + "who_said_cabo": "Wer hat CABO gesagt?", + "kamikaze": "Kamikaze", + "who_has_kamikaze": "Wer hat Kamikaze?", + "done": "Fertig", + "next_round": "Nächste Runde", + "bonus_points_title": "Bonus-Punkte!", + "bonus_points_message": "{playerCount, plural, =1{{names} hat exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommt deshalb {bonusPoints} Punkte abgezogen!} other{{names} haben exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommen deshalb jeweils {bonusPoints} Punkte abgezogen!}}", + "@bonus_points_message": { + "placeholders": { + "playerCount": { + "type": "int" + }, + "names": { + "type": "String" + }, + "pointLimit": { + "type": "int" + }, + "bonusPoints": { + "type": "int" + } + } + }, + + "end_of_game_title": "Spiel beendet", + "end_of_game_message": "{playerCount, plural, =1{{names} hat das Spiel mit {points} Punkten gewonnen. Glückwunsch!} other{{names} haben das Spiel mit {points} Punkten gewonnen. Glückwunsch!}}", + "@end_of_game_message": { + "placeholders": { + "playerCount": { + "type": "int" + }, + "names": { + "type": "String" + }, + "points": { + "type": "int" + } + } + }, + "end_game": "Spiel beenden", + "delete_game": "Spiel löschen", + "new_game_same_settings": "Neues Spiel mit gleichen Einstellungen", + "export_game": "Spiel exportieren", + "id_error_title": "ID Fehler", + "id_error_message": "Das Spiel hat bisher noch keine ID zugewiesen bekommen. Falls du das Spiel löschen möchtest, mache das bitte über das Hauptmenü. Alle neu erstellten Spiele haben eine ID.", + "end_game_title": "Spiel beenden?", + "end_game_message": "Möchtest du das Spiel beenden? Das Spiel wird als beendet markiert und kann nicht fortgeführt werden.", + + "statistics": "Statistiken", + "point_overview": "Punktetabelle", + "scoring_history": "Spielverlauf", + "empty_graph_text": "Du musst mindestens eine Runde spielen, damit der Graph des Spielverlaufes angezeigt werden kann.", + + "settings": "Einstellungen", + "cabo_penalty": "Cabo-Strafe", + "point_limit": "Punkte-Limit", + "standard_mode": "Standard-Modus", + "reset_to_default": "Auf Standard zurücksetzen", + "game_data": "Spieldaten", + "import_data": "Spieldaten importieren", + "export_data": "Spieldaten exportieren", + "delete_data": "Alle Spieldaten löschen", + "delete_data_title": "Spieldaten löschen?", + "delete_data_message": "Bist du sicher, dass du alle Spieldaten löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.", + "app": "App", + + "import_success_title": "Import erfolgreich", + "import_success_message":"Die Spieldaten wurden erfolgreich importiert.", + "import_validation_error_title": "Validierung fehlgeschlagen", + "import_validation_error_message": "Es wurden keine Cabo-Counter Spieldaten gefunden. Bitte stellen Sie sicher, dass es sich um eine gültige Cabo-Counter Exportdatei handelt.", + "import_format_error_title": "Falsches Format", + "import_format_error_message": "Die Datei ist kein gültiges JSON-Format oder enthält ungültige Daten.", + "import_generic_error_title": "Import fehlgeschlagen", + "import_generic_error_message": "Der Import ist fehlgeschlagen.", + + "export_error_title": "Fehler", + "export_error_message": "Datei konnte nicht exportiert werden", + + "error_found": "Fehler gefunden?", + "create_issue": "Issue erstellen", + "wiki": "Wiki", + "app_version": "App-Version", + "privacy_policy": "Datenschutzerklärung", + "loading": "Lädt...", + "build": "Build-Nr." +} diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb new file mode 100644 index 0000000..59c3aa0 --- /dev/null +++ b/lib/l10n/arb/app_en.arb @@ -0,0 +1,167 @@ +{ + "@@locale": "en", + + "app_name": "Cabo Counter", + "round": "Round", + "rounds": "Rounds", + "mode": "Mode", + "points": "Points", + "unlimited": "Unlimited", + "delete": "Delete", + "cancel": "Cancel", + "game": "Game", + "games": "Games", + "gamemode": "Gamemode", + "ok": "OK", + "player": "Player", + "players": "Players", + "name": "Name", + "back": "Back", + + "home": "Home", + + "about": "About", + "licenses": "Licenses", + "license_details": "License Details", + "legal_notice": "Legal Notice", + "no_license_text": "No license available", + + "empty_text_1": "Pretty empty here...", + "empty_text_2": "Create a new game using the button in the top right.", + "delete_game_title": "Delete game?", + "delete_game_message": "Are you sure you want to delete the game \"{gameTitle}\"? This action cannot be undone.", + "@delete_game_message": { + "placeholders": { + "gameTitle": { + "type": "String" + } + } + }, + "pre_rating_title": "Do you like the app?", + "pre_rating_message": "Feedback helps me to continuously improve the app. Thank you!", + "yes": "Yes", + "no": "No", + "bad_rating_title": "Not satisfied?", + "bad_rating_message": "Feel free to send me an email directly so we can solve your problem!", + "contact_email": "Contact via E-Mail", + "email_subject": "Feedback: Cabo Counter App", + "email_body": "I have the following feedback...", + + "overview": "Overview", + "new_game": "New Game", + "game_title": "Game Title", + "select_mode": "Select a mode", + "add_player": "Add Player", + "create_game": "Create Game", + "max_players_title": "Player Limit Reached", + "max_players_message": "You can add a maximum of 5 players.", + "no_gameTitle_title": "Missing Game Title", + "no_gameTitle_message": "Please enter a title for your game.", + "no_mode_title": "Game Mode Required", + "no_mode_message": "Please select a game mode to continue", + "min_players_title": "Too Few Players", + "min_players_message": "At least 2 players are required to start the game.", + "no_name_title": "Missing Player Names", + "no_name_message": "Each player must have a name.", + + "select_game_mode": "Select game mode", + "no_mode_selected": "No mode selected", + "no_default_mode": "No default mode", + "no_default_description": "The default mode gets reset.", + "point_limit_description": "The game ends when a player scores more than {pointLimit} points.", + "@point_limit_description": { + "placeholders": { + "pointLimit": { + "type": "int" + } + } + }, + "unlimited_description": "The game continues until you decide to stop playing. The game can be ended manually at any time.", + + "results": "Results", + "who_said_cabo": "Who called Cabo?", + "kamikaze": "Kamikaze", + "who_has_kamikaze": "Who has Kamikaze?", + "done": "Done", + "next_round": "Next Round", + "bonus_points_title": "Bonus-Points!", + "bonus_points_message": "{playerCount, plural, =1{{names} has reached exactly the point limit of {pointLimit} points and therefore gets {bonusPoints} points deducted!} other{{names} have reached exactly the point limit of {pointLimit} points and therefore get {bonusPoints} points deducted!}}", + "@bonus_points_message": { + "placeholders": { + "playerCount": { + "type": "int" + }, + "names": { + "type": "String" + }, + "pointLimit": { + "type": "int" + }, + "bonusPoints": { + "type": "int" + } + } + }, + + "end_of_game_title": "End of Game", + "end_of_game_message": "{playerCount, plural, =1{{names} won the game with {points} points. Congratulations!} other{{names} won the game with {points} points. Congratulations to everyone!}}", + "@end_of_game_message": { + "placeholders": { + "playerCount": { + "type": "int" + }, + "names": { + "type": "String" + }, + "points": { + "type": "int" + } + } + }, + "end_game": "End Game", + "delete_game": "Delete Game", + "new_game_same_settings": "New Game with same Settings", + "export_game": "Export Game", + "id_error_title": "ID Error", + "id_error_message": "The game has not yet been assigned an ID. If you want to delete the game, please do so via the main menu. All newly created games have an ID.", + "end_game_title": "End the game?", + "end_game_message": "Do you want to end the game? The game gets marked as finished and cannot be continued.", + + "statistics": "Statistics", + "point_overview": "Point Overview", + "scoring_history": "Scoring History", + "empty_graph_text": "You must play at least one round for the game progress graph to be displayed.", + + "settings": "Settings", + "cabo_penalty": "Cabo Penalty", + "point_limit": "Point Limit", + "standard_mode": "Default Mode", + "reset_to_default": "Reset to Default", + "game_data": "Game Data", + "import_data": "Import Data", + "export_data": "Export Data", + "delete_data": "Delete all Game Data", + "delete_data_title": "Delete game data?", + "delete_data_message": "Are you sure you want to delete all game data? This action cannot be undone.", + "app": "App", + + "import_success_title": "Import successful", + "import_success_message":"The game data has been successfully imported.", + "import_validation_error_title": "Validation failed", + "import_validation_error_message": "No Cabo-Counter game data was found. Please make sure that this is a valid Cabo-Counter export file.", + "import_format_error_title": "Wrong format", + "import_format_error_message": "The file is not a valid JSON format or contains invalid data.", + "import_generic_error_title": "Import failed", + "import_generic_error_message": "The import has failed.", + + "export_error_title": "Export failed", + "export_error_message": "Could not export file", + + "error_found": "Found a bug?", + "create_issue": "Create Issue", + "wiki": "Wiki", + "app_version": "App Version", + "privacy_policy": "Privacy Policy", + "loading": "Loading...", + "build": "Build No." +} diff --git a/lib/l10n/untranslated_messages.json b/lib/l10n/arb/untranslated_messages.json similarity index 100% rename from lib/l10n/untranslated_messages.json rename to lib/l10n/arb/untranslated_messages.json diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/generated/app_localizations.dart similarity index 60% rename from lib/l10n/app_localizations.dart rename to lib/l10n/generated/app_localizations.dart index 1751ab7..0ca7043 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -18,7 +18,7 @@ import 'app_localizations_en.dart'; /// `supportedLocales` list. For example: /// /// ```dart -/// import 'l10n/app_localizations.dart'; +/// import 'generated/app_localizations.dart'; /// /// return MaterialApp( /// localizationsDelegates: AppLocalizations.localizationsDelegates, @@ -152,6 +152,18 @@ abstract class AppLocalizations { /// **'Spiel'** String get game; + /// No description provided for @games. + /// + /// In de, this message translates to: + /// **'Spiele'** + String get games; + + /// No description provided for @gamemode. + /// + /// In de, this message translates to: + /// **'Spielmodus'** + String get gamemode; + /// No description provided for @ok. /// /// In de, this message translates to: @@ -194,6 +206,30 @@ abstract class AppLocalizations { /// **'Über'** String get about; + /// No description provided for @licenses. + /// + /// In de, this message translates to: + /// **'Lizenzen'** + String get licenses; + + /// No description provided for @license_details. + /// + /// In de, this message translates to: + /// **'Lizenzdetails'** + String get license_details; + + /// No description provided for @no_license_text. + /// + /// In de, this message translates to: + /// **'Keine Lizenz verfügbar'** + String get no_license_text; + + /// No description provided for @legal_notice. + /// + /// In de, this message translates to: + /// **'Impressum'** + String get legal_notice; + /// No description provided for @empty_text_1. /// /// In de, this message translates to: @@ -215,9 +251,63 @@ abstract class AppLocalizations { /// No description provided for @delete_game_message. /// /// In de, this message translates to: - /// **'Bist du sicher, dass du die Runde {gameTitle} löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'** + /// **'Bist du sicher, dass du das Spiel \"{gameTitle}\" löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'** String delete_game_message(String gameTitle); + /// No description provided for @pre_rating_title. + /// + /// In de, this message translates to: + /// **'Gefällt dir die App?'** + String get pre_rating_title; + + /// No description provided for @pre_rating_message. + /// + /// In de, this message translates to: + /// **'Feedback hilft mir, die App zu verbessern. Vielen Dank!'** + String get pre_rating_message; + + /// No description provided for @yes. + /// + /// In de, this message translates to: + /// **'Ja'** + String get yes; + + /// No description provided for @no. + /// + /// In de, this message translates to: + /// **'Nein'** + String get no; + + /// No description provided for @bad_rating_title. + /// + /// In de, this message translates to: + /// **'Unzufrieden mit der App?'** + String get bad_rating_title; + + /// No description provided for @bad_rating_message. + /// + /// In de, this message translates to: + /// **'Schreib mir gerne direkt eine E-Mail, damit wir dein Problem lösen können!'** + String get bad_rating_message; + + /// No description provided for @contact_email. + /// + /// In de, this message translates to: + /// **'E-Mail schreiben'** + String get contact_email; + + /// No description provided for @email_subject. + /// + /// In de, this message translates to: + /// **'Feedback: Cabo Counter App'** + String get email_subject; + + /// No description provided for @email_body. + /// + /// In de, this message translates to: + /// **'Ich habe folgendes Feedback...'** + String get email_body; + /// No description provided for @overview. /// /// In de, this message translates to: @@ -311,7 +401,7 @@ abstract class AppLocalizations { /// No description provided for @no_name_message. /// /// In de, this message translates to: - /// **'Jeder Spieler muss einen Namen haben.'** + /// **'Jede:r Spieler:in muss einen Namen haben.'** String get no_name_message; /// No description provided for @select_game_mode. @@ -320,6 +410,24 @@ abstract class AppLocalizations { /// **'Spielmodus auswählen'** String get select_game_mode; + /// No description provided for @no_mode_selected. + /// + /// In de, this message translates to: + /// **'Wähle einen Spielmodus'** + String get no_mode_selected; + + /// No description provided for @no_default_mode. + /// + /// In de, this message translates to: + /// **'Kein Modus'** + String get no_default_mode; + + /// No description provided for @no_default_description. + /// + /// In de, this message translates to: + /// **'Entscheide bei jedem Spiel selber, welchen Modus du spielen möchtest.'** + String get no_default_description; + /// No description provided for @point_limit_description. /// /// In de, this message translates to: @@ -329,7 +437,7 @@ abstract class AppLocalizations { /// No description provided for @unlimited_description. /// /// In de, this message translates to: - /// **'Dem Spiel sind keine Grenzen gesetzt. Es wird so lange gespielt, bis ihr keine Lust mehr habt.'** + /// **'Es wird so lange gespielt, bis ihr keine Lust mehr habt. Das Spiel kann jederzeit manuell beendet werden.'** String get unlimited_description; /// No description provided for @results. @@ -350,6 +458,12 @@ abstract class AppLocalizations { /// **'Kamikaze'** String get kamikaze; + /// No description provided for @who_has_kamikaze. + /// + /// In de, this message translates to: + /// **'Wer hat Kamikaze?'** + String get who_has_kamikaze; + /// No description provided for @done. /// /// In de, this message translates to: @@ -362,11 +476,36 @@ abstract class AppLocalizations { /// **'Nächste Runde'** String get next_round; - /// No description provided for @statistics. + /// No description provided for @bonus_points_title. /// /// In de, this message translates to: - /// **'Statistiken'** - String get statistics; + /// **'Bonus-Punkte!'** + String get bonus_points_title; + + /// No description provided for @bonus_points_message. + /// + /// In de, this message translates to: + /// **'{playerCount, plural, =1{{names} hat exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommt deshalb {bonusPoints} Punkte abgezogen!} other{{names} haben exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommen deshalb jeweils {bonusPoints} Punkte abgezogen!}}'** + String bonus_points_message( + int playerCount, String names, int pointLimit, int bonusPoints); + + /// No description provided for @end_of_game_title. + /// + /// In de, this message translates to: + /// **'Spiel beendet'** + String get end_of_game_title; + + /// No description provided for @end_of_game_message. + /// + /// In de, this message translates to: + /// **'{playerCount, plural, =1{{names} hat das Spiel mit {points} Punkten gewonnen. Glückwunsch!} other{{names} haben das Spiel mit {points} Punkten gewonnen. Glückwunsch!}}'** + String end_of_game_message(int playerCount, String names, int points); + + /// No description provided for @end_game. + /// + /// In de, this message translates to: + /// **'Spiel beenden'** + String get end_game; /// No description provided for @delete_game. /// @@ -386,11 +525,53 @@ abstract class AppLocalizations { /// **'Spiel exportieren'** String get export_game; - /// No description provided for @game_process. + /// No description provided for @id_error_title. + /// + /// In de, this message translates to: + /// **'ID Fehler'** + String get id_error_title; + + /// No description provided for @id_error_message. + /// + /// In de, this message translates to: + /// **'Das Spiel hat bisher noch keine ID zugewiesen bekommen. Falls du das Spiel löschen möchtest, mache das bitte über das Hauptmenü. Alle neu erstellten Spiele haben eine ID.'** + String get id_error_message; + + /// No description provided for @end_game_title. + /// + /// In de, this message translates to: + /// **'Spiel beenden?'** + String get end_game_title; + + /// No description provided for @end_game_message. + /// + /// In de, this message translates to: + /// **'Möchtest du das Spiel beenden? Das Spiel wird als beendet markiert und kann nicht fortgeführt werden.'** + String get end_game_message; + + /// No description provided for @statistics. + /// + /// In de, this message translates to: + /// **'Statistiken'** + String get statistics; + + /// No description provided for @point_overview. + /// + /// In de, this message translates to: + /// **'Punktetabelle'** + String get point_overview; + + /// No description provided for @scoring_history. /// /// In de, this message translates to: /// **'Spielverlauf'** - String get game_process; + String get scoring_history; + + /// No description provided for @empty_graph_text. + /// + /// In de, this message translates to: + /// **'Du musst mindestens eine Runde spielen, damit der Graph des Spielverlaufes angezeigt werden kann.'** + String get empty_graph_text; /// No description provided for @settings. /// @@ -404,23 +585,17 @@ abstract class AppLocalizations { /// **'Cabo-Strafe'** String get cabo_penalty; - /// No description provided for @cabo_penalty_subtitle. - /// - /// In de, this message translates to: - /// **'... für falsches Cabo sagen'** - String get cabo_penalty_subtitle; - /// No description provided for @point_limit. /// /// In de, this message translates to: /// **'Punkte-Limit'** String get point_limit; - /// No description provided for @point_limit_subtitle. + /// No description provided for @standard_mode. /// /// In de, this message translates to: - /// **'... hier ist Schluss'** - String get point_limit_subtitle; + /// **'Standard-Modus'** + String get standard_mode; /// No description provided for @reset_to_default. /// @@ -437,32 +612,98 @@ abstract class AppLocalizations { /// No description provided for @import_data. /// /// In de, this message translates to: - /// **'Daten importieren'** + /// **'Spieldaten importieren'** String get import_data; /// No description provided for @export_data. /// /// In de, this message translates to: - /// **'Daten exportieren'** + /// **'Spieldaten exportieren'** String get export_data; - /// No description provided for @error. + /// No description provided for @delete_data. + /// + /// In de, this message translates to: + /// **'Alle Spieldaten löschen'** + String get delete_data; + + /// No description provided for @delete_data_title. + /// + /// In de, this message translates to: + /// **'Spieldaten löschen?'** + String get delete_data_title; + + /// No description provided for @delete_data_message. + /// + /// In de, this message translates to: + /// **'Bist du sicher, dass du alle Spieldaten löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'** + String get delete_data_message; + + /// No description provided for @app. + /// + /// In de, this message translates to: + /// **'App'** + String get app; + + /// No description provided for @import_success_title. + /// + /// In de, this message translates to: + /// **'Import erfolgreich'** + String get import_success_title; + + /// No description provided for @import_success_message. + /// + /// In de, this message translates to: + /// **'Die Spieldaten wurden erfolgreich importiert.'** + String get import_success_message; + + /// No description provided for @import_validation_error_title. + /// + /// In de, this message translates to: + /// **'Validierung fehlgeschlagen'** + String get import_validation_error_title; + + /// No description provided for @import_validation_error_message. + /// + /// In de, this message translates to: + /// **'Es wurden keine Cabo-Counter Spieldaten gefunden. Bitte stellen Sie sicher, dass es sich um eine gültige Cabo-Counter Exportdatei handelt.'** + String get import_validation_error_message; + + /// No description provided for @import_format_error_title. + /// + /// In de, this message translates to: + /// **'Falsches Format'** + String get import_format_error_title; + + /// No description provided for @import_format_error_message. + /// + /// In de, this message translates to: + /// **'Die Datei ist kein gültiges JSON-Format oder enthält ungültige Daten.'** + String get import_format_error_message; + + /// No description provided for @import_generic_error_title. + /// + /// In de, this message translates to: + /// **'Import fehlgeschlagen'** + String get import_generic_error_title; + + /// No description provided for @import_generic_error_message. + /// + /// In de, this message translates to: + /// **'Der Import ist fehlgeschlagen.'** + String get import_generic_error_message; + + /// No description provided for @export_error_title. /// /// In de, this message translates to: /// **'Fehler'** - String get error; + String get export_error_title; - /// No description provided for @error_import. - /// - /// In de, this message translates to: - /// **'Datei konnte nicht importiert werden'** - String get error_import; - - /// No description provided for @error_export. + /// No description provided for @export_error_message. /// /// In de, this message translates to: /// **'Datei konnte nicht exportiert werden'** - String get error_export; + String get export_error_message; /// No description provided for @error_found. /// @@ -476,29 +717,35 @@ abstract class AppLocalizations { /// **'Issue erstellen'** String get create_issue; + /// No description provided for @wiki. + /// + /// In de, this message translates to: + /// **'Wiki'** + String get wiki; + /// No description provided for @app_version. /// /// In de, this message translates to: /// **'App-Version'** String get app_version; + /// No description provided for @privacy_policy. + /// + /// In de, this message translates to: + /// **'Datenschutzerklärung'** + String get privacy_policy; + + /// No description provided for @loading. + /// + /// In de, this message translates to: + /// **'Lädt...'** + String get loading; + /// No description provided for @build. /// /// In de, this message translates to: - /// **'Build'** + /// **'Build-Nr.'** String get build; - - /// No description provided for @load_version. - /// - /// In de, this message translates to: - /// **'Lade Version...'** - String get load_version; - - /// No description provided for @about_text. - /// - /// In de, this message translates to: - /// **'Hey :) Danke, dass du als eine:r der ersten User meiner ersten eigenen App dabei bist! Ich hab sehr viel Arbeit in dieses Projekt gesteckt und auch, wenn ich (hoffentlich) an vieles Gedacht hab, wird auf jeden Fall noch nicht alles 100% funktionieren. Solltest du also irgendwelche Fehler entdecken oder Feedback zum Design oder der Benutzerfreundlichkeit haben, teile Sie mir gern über die Testflight App oder auf den dir bekannten Wegen mit. Danke! '** - String get about_text; } class _AppLocalizationsDelegate diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart new file mode 100644 index 0000000..85d89ad --- /dev/null +++ b/lib/l10n/generated/app_localizations_de.dart @@ -0,0 +1,373 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for German (`de`). +class AppLocalizationsDe extends AppLocalizations { + AppLocalizationsDe([String locale = 'de']) : super(locale); + + @override + String get app_name => 'Cabo Counter'; + + @override + String get round => 'Runde'; + + @override + String get rounds => 'Runden'; + + @override + String get mode => 'Modus'; + + @override + String get points => 'Punkte'; + + @override + String get unlimited => 'Unbegrenzt'; + + @override + String get delete => 'Löschen'; + + @override + String get cancel => 'Abbrechen'; + + @override + String get game => 'Spiel'; + + @override + String get games => 'Spiele'; + + @override + String get gamemode => 'Spielmodus'; + + @override + String get ok => 'OK'; + + @override + String get player => 'Spieler:in'; + + @override + String get players => 'Spieler:innen'; + + @override + String get name => 'Name'; + + @override + String get back => 'Zurück'; + + @override + String get home => 'Home'; + + @override + String get about => 'Über'; + + @override + String get licenses => 'Lizenzen'; + + @override + String get license_details => 'Lizenzdetails'; + + @override + String get no_license_text => 'Keine Lizenz verfügbar'; + + @override + String get legal_notice => 'Impressum'; + + @override + String get empty_text_1 => 'Ganz schön leer hier...'; + + @override + String get empty_text_2 => + 'Füge über den Button oben rechts eine neue Runde hinzu'; + + @override + String get delete_game_title => 'Spiel löschen?'; + + @override + String delete_game_message(String gameTitle) { + return 'Bist du sicher, dass du das Spiel \"$gameTitle\" löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'; + } + + @override + String get pre_rating_title => 'Gefällt dir die App?'; + + @override + String get pre_rating_message => + 'Feedback hilft mir, die App zu verbessern. Vielen Dank!'; + + @override + String get yes => 'Ja'; + + @override + String get no => 'Nein'; + + @override + String get bad_rating_title => 'Unzufrieden mit der App?'; + + @override + String get bad_rating_message => + 'Schreib mir gerne direkt eine E-Mail, damit wir dein Problem lösen können!'; + + @override + String get contact_email => 'E-Mail schreiben'; + + @override + String get email_subject => 'Feedback: Cabo Counter App'; + + @override + String get email_body => 'Ich habe folgendes Feedback...'; + + @override + String get overview => 'Übersicht'; + + @override + String get new_game => 'Neues Spiel'; + + @override + String get game_title => 'Titel des Spiels'; + + @override + String get select_mode => 'Wähle einen Modus'; + + @override + String get add_player => 'Spieler:in hinzufügen'; + + @override + String get create_game => 'Spiel erstellen'; + + @override + String get max_players_title => 'Maximale Anzahl erreicht'; + + @override + String get max_players_message => + 'Es können maximal 5 Spieler:innen hinzugefügt werden.'; + + @override + String get no_gameTitle_title => 'Kein Titel'; + + @override + String get no_gameTitle_message => + 'Es muss ein Titel für das Spiel eingegeben werden.'; + + @override + String get no_mode_title => 'Kein Modus'; + + @override + String get no_mode_message => 'Es muss ein Spielmodus ausgewählt werden.'; + + @override + String get min_players_title => 'Zu wenig Spieler:innen'; + + @override + String get min_players_message => + 'Es müssen mindestens 2 Spieler:innen hinzugefügt werden'; + + @override + String get no_name_title => 'Kein Name'; + + @override + String get no_name_message => 'Jede:r Spieler:in muss einen Namen haben.'; + + @override + String get select_game_mode => 'Spielmodus auswählen'; + + @override + String get no_mode_selected => 'Wähle einen Spielmodus'; + + @override + String get no_default_mode => 'Kein Modus'; + + @override + String get no_default_description => + 'Entscheide bei jedem Spiel selber, welchen Modus du spielen möchtest.'; + + @override + String point_limit_description(int pointLimit) { + return 'Es wird so lange gespielt, bis ein:e Spieler:in mehr als $pointLimit Punkte erreicht'; + } + + @override + String get unlimited_description => + 'Es wird so lange gespielt, bis ihr keine Lust mehr habt. Das Spiel kann jederzeit manuell beendet werden.'; + + @override + String get results => 'Ergebnisse'; + + @override + String get who_said_cabo => 'Wer hat CABO gesagt?'; + + @override + String get kamikaze => 'Kamikaze'; + + @override + String get who_has_kamikaze => 'Wer hat Kamikaze?'; + + @override + String get done => 'Fertig'; + + @override + String get next_round => 'Nächste Runde'; + + @override + String get bonus_points_title => 'Bonus-Punkte!'; + + @override + String bonus_points_message( + int playerCount, String names, int pointLimit, int bonusPoints) { + String _temp0 = intl.Intl.pluralLogic( + playerCount, + locale: localeName, + other: + '$names haben exakt das Punktelimit von $pointLimit Punkten erreicht und bekommen deshalb jeweils $bonusPoints Punkte abgezogen!', + one: + '$names hat exakt das Punktelimit von $pointLimit Punkten erreicht und bekommt deshalb $bonusPoints Punkte abgezogen!', + ); + return '$_temp0'; + } + + @override + String get end_of_game_title => 'Spiel beendet'; + + @override + String end_of_game_message(int playerCount, String names, int points) { + String _temp0 = intl.Intl.pluralLogic( + playerCount, + locale: localeName, + other: + '$names haben das Spiel mit $points Punkten gewonnen. Glückwunsch!', + one: '$names hat das Spiel mit $points Punkten gewonnen. Glückwunsch!', + ); + return '$_temp0'; + } + + @override + String get end_game => 'Spiel beenden'; + + @override + String get delete_game => 'Spiel löschen'; + + @override + String get new_game_same_settings => 'Neues Spiel mit gleichen Einstellungen'; + + @override + String get export_game => 'Spiel exportieren'; + + @override + String get id_error_title => 'ID Fehler'; + + @override + String get id_error_message => + 'Das Spiel hat bisher noch keine ID zugewiesen bekommen. Falls du das Spiel löschen möchtest, mache das bitte über das Hauptmenü. Alle neu erstellten Spiele haben eine ID.'; + + @override + String get end_game_title => 'Spiel beenden?'; + + @override + String get end_game_message => + 'Möchtest du das Spiel beenden? Das Spiel wird als beendet markiert und kann nicht fortgeführt werden.'; + + @override + String get statistics => 'Statistiken'; + + @override + String get point_overview => 'Punktetabelle'; + + @override + String get scoring_history => 'Spielverlauf'; + + @override + String get empty_graph_text => + 'Du musst mindestens eine Runde spielen, damit der Graph des Spielverlaufes angezeigt werden kann.'; + + @override + String get settings => 'Einstellungen'; + + @override + String get cabo_penalty => 'Cabo-Strafe'; + + @override + String get point_limit => 'Punkte-Limit'; + + @override + String get standard_mode => 'Standard-Modus'; + + @override + String get reset_to_default => 'Auf Standard zurücksetzen'; + + @override + String get game_data => 'Spieldaten'; + + @override + String get import_data => 'Spieldaten importieren'; + + @override + String get export_data => 'Spieldaten exportieren'; + + @override + String get delete_data => 'Alle Spieldaten löschen'; + + @override + String get delete_data_title => 'Spieldaten löschen?'; + + @override + String get delete_data_message => + 'Bist du sicher, dass du alle Spieldaten löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'; + + @override + String get app => 'App'; + + @override + String get import_success_title => 'Import erfolgreich'; + + @override + String get import_success_message => + 'Die Spieldaten wurden erfolgreich importiert.'; + + @override + String get import_validation_error_title => 'Validierung fehlgeschlagen'; + + @override + String get import_validation_error_message => + 'Es wurden keine Cabo-Counter Spieldaten gefunden. Bitte stellen Sie sicher, dass es sich um eine gültige Cabo-Counter Exportdatei handelt.'; + + @override + String get import_format_error_title => 'Falsches Format'; + + @override + String get import_format_error_message => + 'Die Datei ist kein gültiges JSON-Format oder enthält ungültige Daten.'; + + @override + String get import_generic_error_title => 'Import fehlgeschlagen'; + + @override + String get import_generic_error_message => 'Der Import ist fehlgeschlagen.'; + + @override + String get export_error_title => 'Fehler'; + + @override + String get export_error_message => 'Datei konnte nicht exportiert werden'; + + @override + String get error_found => 'Fehler gefunden?'; + + @override + String get create_issue => 'Issue erstellen'; + + @override + String get wiki => 'Wiki'; + + @override + String get app_version => 'App-Version'; + + @override + String get privacy_policy => 'Datenschutzerklärung'; + + @override + String get loading => 'Lädt...'; + + @override + String get build => 'Build-Nr.'; +} diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart new file mode 100644 index 0000000..cfbc4c0 --- /dev/null +++ b/lib/l10n/generated/app_localizations_en.dart @@ -0,0 +1,370 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get app_name => 'Cabo Counter'; + + @override + String get round => 'Round'; + + @override + String get rounds => 'Rounds'; + + @override + String get mode => 'Mode'; + + @override + String get points => 'Points'; + + @override + String get unlimited => 'Unlimited'; + + @override + String get delete => 'Delete'; + + @override + String get cancel => 'Cancel'; + + @override + String get game => 'Game'; + + @override + String get games => 'Games'; + + @override + String get gamemode => 'Gamemode'; + + @override + String get ok => 'OK'; + + @override + String get player => 'Player'; + + @override + String get players => 'Players'; + + @override + String get name => 'Name'; + + @override + String get back => 'Back'; + + @override + String get home => 'Home'; + + @override + String get about => 'About'; + + @override + String get licenses => 'Licenses'; + + @override + String get license_details => 'License Details'; + + @override + String get no_license_text => 'No license available'; + + @override + String get legal_notice => 'Legal Notice'; + + @override + String get empty_text_1 => 'Pretty empty here...'; + + @override + String get empty_text_2 => + 'Create a new game using the button in the top right.'; + + @override + String get delete_game_title => 'Delete game?'; + + @override + String delete_game_message(String gameTitle) { + return 'Are you sure you want to delete the game \"$gameTitle\"? This action cannot be undone.'; + } + + @override + String get pre_rating_title => 'Do you like the app?'; + + @override + String get pre_rating_message => + 'Feedback helps me to continuously improve the app. Thank you!'; + + @override + String get yes => 'Yes'; + + @override + String get no => 'No'; + + @override + String get bad_rating_title => 'Not satisfied?'; + + @override + String get bad_rating_message => + 'Feel free to send me an email directly so we can solve your problem!'; + + @override + String get contact_email => 'Contact via E-Mail'; + + @override + String get email_subject => 'Feedback: Cabo Counter App'; + + @override + String get email_body => 'I have the following feedback...'; + + @override + String get overview => 'Overview'; + + @override + String get new_game => 'New Game'; + + @override + String get game_title => 'Game Title'; + + @override + String get select_mode => 'Select a mode'; + + @override + String get add_player => 'Add Player'; + + @override + String get create_game => 'Create Game'; + + @override + String get max_players_title => 'Player Limit Reached'; + + @override + String get max_players_message => 'You can add a maximum of 5 players.'; + + @override + String get no_gameTitle_title => 'Missing Game Title'; + + @override + String get no_gameTitle_message => 'Please enter a title for your game.'; + + @override + String get no_mode_title => 'Game Mode Required'; + + @override + String get no_mode_message => 'Please select a game mode to continue'; + + @override + String get min_players_title => 'Too Few Players'; + + @override + String get min_players_message => + 'At least 2 players are required to start the game.'; + + @override + String get no_name_title => 'Missing Player Names'; + + @override + String get no_name_message => 'Each player must have a name.'; + + @override + String get select_game_mode => 'Select game mode'; + + @override + String get no_mode_selected => 'No mode selected'; + + @override + String get no_default_mode => 'No default mode'; + + @override + String get no_default_description => 'The default mode gets reset.'; + + @override + String point_limit_description(int pointLimit) { + return 'The game ends when a player scores more than $pointLimit points.'; + } + + @override + String get unlimited_description => + 'The game continues until you decide to stop playing. The game can be ended manually at any time.'; + + @override + String get results => 'Results'; + + @override + String get who_said_cabo => 'Who called Cabo?'; + + @override + String get kamikaze => 'Kamikaze'; + + @override + String get who_has_kamikaze => 'Who has Kamikaze?'; + + @override + String get done => 'Done'; + + @override + String get next_round => 'Next Round'; + + @override + String get bonus_points_title => 'Bonus-Points!'; + + @override + String bonus_points_message( + int playerCount, String names, int pointLimit, int bonusPoints) { + String _temp0 = intl.Intl.pluralLogic( + playerCount, + locale: localeName, + other: + '$names have reached exactly the point limit of $pointLimit points and therefore get $bonusPoints points deducted!', + one: + '$names has reached exactly the point limit of $pointLimit points and therefore gets $bonusPoints points deducted!', + ); + return '$_temp0'; + } + + @override + String get end_of_game_title => 'End of Game'; + + @override + String end_of_game_message(int playerCount, String names, int points) { + String _temp0 = intl.Intl.pluralLogic( + playerCount, + locale: localeName, + other: + '$names won the game with $points points. Congratulations to everyone!', + one: '$names won the game with $points points. Congratulations!', + ); + return '$_temp0'; + } + + @override + String get end_game => 'End Game'; + + @override + String get delete_game => 'Delete Game'; + + @override + String get new_game_same_settings => 'New Game with same Settings'; + + @override + String get export_game => 'Export Game'; + + @override + String get id_error_title => 'ID Error'; + + @override + String get id_error_message => + 'The game has not yet been assigned an ID. If you want to delete the game, please do so via the main menu. All newly created games have an ID.'; + + @override + String get end_game_title => 'End the game?'; + + @override + String get end_game_message => + 'Do you want to end the game? The game gets marked as finished and cannot be continued.'; + + @override + String get statistics => 'Statistics'; + + @override + String get point_overview => 'Point Overview'; + + @override + String get scoring_history => 'Scoring History'; + + @override + String get empty_graph_text => + 'You must play at least one round for the game progress graph to be displayed.'; + + @override + String get settings => 'Settings'; + + @override + String get cabo_penalty => 'Cabo Penalty'; + + @override + String get point_limit => 'Point Limit'; + + @override + String get standard_mode => 'Default Mode'; + + @override + String get reset_to_default => 'Reset to Default'; + + @override + String get game_data => 'Game Data'; + + @override + String get import_data => 'Import Data'; + + @override + String get export_data => 'Export Data'; + + @override + String get delete_data => 'Delete all Game Data'; + + @override + String get delete_data_title => 'Delete game data?'; + + @override + String get delete_data_message => + 'Are you sure you want to delete all game data? This action cannot be undone.'; + + @override + String get app => 'App'; + + @override + String get import_success_title => 'Import successful'; + + @override + String get import_success_message => + 'The game data has been successfully imported.'; + + @override + String get import_validation_error_title => 'Validation failed'; + + @override + String get import_validation_error_message => + 'No Cabo-Counter game data was found. Please make sure that this is a valid Cabo-Counter export file.'; + + @override + String get import_format_error_title => 'Wrong format'; + + @override + String get import_format_error_message => + 'The file is not a valid JSON format or contains invalid data.'; + + @override + String get import_generic_error_title => 'Import failed'; + + @override + String get import_generic_error_message => 'The import has failed.'; + + @override + String get export_error_title => 'Export failed'; + + @override + String get export_error_message => 'Could not export file'; + + @override + String get error_found => 'Found a bug?'; + + @override + String get create_issue => 'Create Issue'; + + @override + String get wiki => 'Wiki'; + + @override + String get app_version => 'App Version'; + + @override + String get privacy_policy => 'Privacy Policy'; + + @override + String get loading => 'Loading...'; + + @override + String get build => 'Build No.'; +} diff --git a/lib/main.dart b/lib/main.dart index 27c3835..adaffdb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,19 +1,21 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/tab_view.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/utility/globals.dart'; -import 'package:cabo_counter/views/tab_view.dart'; +import 'package:cabo_counter/services/version_service.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); + // Ensure the app runs in portrait mode only await SystemChrome.setPreferredOrientations( [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); + + // Initialize services await ConfigService.initConfig(); - Globals.pointLimit = await ConfigService.getPointLimit(); - Globals.caboPenalty = await ConfigService.getCaboPenalty(); + await VersionService.init(); runApp(const App()); } @@ -39,6 +41,9 @@ class _AppState extends State with WidgetsBindingObserver { } @override + + /// Every time the app goes into the background or is closed, + /// save the current game sessions to local storage. void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.paused || state == AppLifecycleState.detached) { @@ -65,6 +70,7 @@ class _AppState extends State with WidgetsBindingObserver { return supportedLocales.first; }, theme: CupertinoThemeData( + applyThemeToAll: true, brightness: Brightness.dark, primaryColor: CustomTheme.primaryColor, scaffoldBackgroundColor: CustomTheme.backgroundColor, diff --git a/lib/presentation/views/about/about_view.dart b/lib/presentation/views/about/about_view.dart new file mode 100644 index 0000000..7ea1609 --- /dev/null +++ b/lib/presentation/views/about/about_view.dart @@ -0,0 +1,95 @@ +import 'package:cabo_counter/core/constants.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/about/licenses/license_view.dart'; +import 'package:cabo_counter/services/version_service.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:url_launcher/url_launcher.dart'; + +/// A view that displays information about the app, including its name, version, +/// privacy policy, imprint, and licenses. +class AboutView extends StatelessWidget { + const AboutView({super.key}); + + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + resizeToAvoidBottomInset: false, + navigationBar: CupertinoNavigationBar( + middle: Text(AppLocalizations.of(context).about), + ), + child: SafeArea( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 0, 0), + child: Text( + AppLocalizations.of(context).app_name, + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + ), + ), + ), + Text( + '${AppLocalizations.of(context).app_version} ${VersionService.getVersionWithBuild()}', + style: TextStyle(fontSize: 15, color: Colors.grey[300]), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + child: SizedBox( + height: 200, + child: Image.asset('assets/cabo_counter-logo_rounded.png'), + )), + CupertinoButton( + sizeStyle: CupertinoButtonSize.medium, + padding: EdgeInsets.zero, + child: Text(AppLocalizations.of(context).privacy_policy), + onPressed: () => + launchUrl(Uri.parse(Constants.kPrivacyPolicyLink)), + ), + CupertinoButton( + sizeStyle: CupertinoButtonSize.medium, + padding: EdgeInsets.zero, + child: Text(AppLocalizations.of(context).legal_notice), + onPressed: () => launchUrl(Uri.parse(Constants.kLegalLink)), + ), + CupertinoButton( + sizeStyle: CupertinoButtonSize.medium, + padding: EdgeInsets.zero, + child: Text(AppLocalizations.of(context).licenses), + onPressed: () => Navigator.push(context, + CupertinoPageRoute(builder: (_) => const LicenseView()))), + const SizedBox( + height: 10, + ), + const Text( + '\u00A9 Felix Kirchner', + style: TextStyle(fontSize: 16), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + onPressed: () => + launchUrl(Uri.parse(Constants.kInstagramLink)), + icon: const Icon(FontAwesomeIcons.instagram)), + IconButton( + onPressed: () => + launchUrl(Uri.parse('mailto:${Constants.kEmail}')), + icon: const Icon(CupertinoIcons.envelope)), + IconButton( + onPressed: () => + launchUrl(Uri.parse(Constants.kGithubLink)), + icon: const Icon(FontAwesomeIcons.github)), + ], + ), + ], + ), + ))); + } +} diff --git a/lib/presentation/views/about/licenses/license_detail_view.dart b/lib/presentation/views/about/licenses/license_detail_view.dart new file mode 100644 index 0000000..16b75e3 --- /dev/null +++ b/lib/presentation/views/about/licenses/license_detail_view.dart @@ -0,0 +1,65 @@ +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart' + show AppLocalizations; +import 'package:flutter/cupertino.dart'; + +/// Displays the details of a specific open source software license in a Cupertino-style view. +/// +/// This view presents the license title and its full text in a scrollable layout. +/// +/// Required parameters: +/// - [title]: The name of the license. +/// - [license]: The full license text to display. +class LicenseDetailView extends StatelessWidget { + final String title, license; + const LicenseDetailView( + {super.key, required this.title, required this.license}); + + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text( + AppLocalizations.of(context).license_details, + ), + previousPageTitle: AppLocalizations.of(context).licenses, + ), + child: SafeArea( + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + children: [ + Padding( + padding: + const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + child: FittedBox( + fit: BoxFit.fill, + child: Text( + title, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + Container( + margin: const EdgeInsets.all(8), + padding: + const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + decoration: BoxDecoration( + color: CustomTheme.buttonBackgroundColor, + borderRadius: BorderRadius.circular(16)), + child: Text( + license, + style: const TextStyle(fontSize: 15), + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/views/about/licenses/license_view.dart b/lib/presentation/views/about/licenses/license_view.dart new file mode 100644 index 0000000..6fecbde --- /dev/null +++ b/lib/presentation/views/about/licenses/license_view.dart @@ -0,0 +1,67 @@ +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/about/licenses/license_detail_view.dart'; +import 'package:cabo_counter/presentation/views/about/licenses/oss_licenses.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +/// Displays a list of open source software licenses used in the app. +/// +/// Users can tap on a license to view its details on a separate screen. +/// This view uses a Cupertino design and supports localization. +/// +/// See also: +/// - [LicenseDetailView] for displaying license details. +/// - [ossLicenses] for the list of licenses. +class LicenseView extends StatelessWidget { + const LicenseView({super.key}); + + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text(AppLocalizations.of(context).licenses), + previousPageTitle: AppLocalizations.of(context).about, + ), + child: SafeArea( + child: ListView.builder( + physics: const BouncingScrollPhysics(), + itemCount: ossLicenses.length, + itemBuilder: (_, index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4), + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(8), + ), + child: CupertinoListTile( + backgroundColor: CustomTheme.backgroundColor, + onTap: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => LicenseDetailView( + title: ossLicenses[index].name, + license: ossLicenses[index].license ?? + AppLocalizations.of(context).no_license_text, + ), + ), + ); + }, + trailing: const CupertinoListTileChevron(), + title: Text( + ossLicenses[index].name, + style: GoogleFonts.roboto(), + ), + subtitle: Text(ossLicenses[index].description), + ), + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/presentation/views/about/licenses/oss_licenses.dart b/lib/presentation/views/about/licenses/oss_licenses.dart new file mode 100644 index 0000000..dfd9ded --- /dev/null +++ b/lib/presentation/views/about/licenses/oss_licenses.dart @@ -0,0 +1,5899 @@ +// cSpell:disable + +/// This code was generated by flutter_oss_licenses +/// https://pub.dev/packages/flutter_oss_licenses +const ossLicenses = [ + Package( + name: '_fe_analyzer_shared', + description: 'Logic that is shared between the front_end and analyzer packages.', + repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/_fe_analyzer_shared', + authors: [], + version: '85.0.0', + license: '''Copyright 2019, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'analyzer', + description: 'This package provides a library that performs static analysis of Dart code.', + repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/analyzer', + authors: [], + version: '7.7.1', + license: '''Copyright 2013, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'args', + description: 'Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/args', + authors: [], + version: '2.7.0', + license: '''Copyright 2013, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'async', + description: "Utility functions and classes related to the 'dart:async' library.", + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/async', + authors: [], + version: '2.13.0', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'boolean_selector', + description: "A flexible syntax for boolean expressions, based on a simplified version of Dart's expression syntax.", + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/boolean_selector', + authors: [], + version: '2.1.2', + license: '''Copyright 2016, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'characters', + description: 'String replacement with operations that are Unicode/grapheme cluster aware.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/characters', + authors: [], + version: '1.4.0', + license: '''Copyright 2019, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'cli_config', + description: 'A library to take config values from configuration files, CLI arguments, and environment variables.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/cli_config', + authors: [], + version: '0.2.0', + license: '''Copyright 2023, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'clock', + description: 'A fakeable wrapper for dart:core clock APIs.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/clock', + authors: [], + version: '1.1.2', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'collection', + description: 'Collections and utilities functions and classes related to collections.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/collection', + authors: [], + version: '1.19.1', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'confetti', + description: 'Blast colorful confetti all over the screen. Celebrate in app achievements with style. Control the velocity, angle, gravity and amount of confetti.', + homepage: 'https://github.com/funwithflutter/flutter_confetti', + authors: [], + version: '0.6.0', + license: '''The MIT License (MIT) +Copyright (c) 2018 Felix Angelov + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'convert', + description: 'Utilities for converting between data representations. Provides a number of Sink, Codec, Decoder, and Encoder types.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/convert', + authors: [], + version: '3.1.2', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'coverage', + description: 'Coverage data manipulation and formatting', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/coverage', + authors: [], + version: '1.15.0', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'cross_file', + description: 'An abstraction to allow working with files across multiple platforms.', + repository: 'https://github.com/flutter/packages/tree/main/packages/cross_file', + authors: [], + version: '0.3.4+2', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'crypto', + description: 'Implementations of SHA, MD5, and HMAC cryptographic functions.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/crypto', + authors: [], + version: '3.0.6', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'cupertino_icons', + description: 'Default icons asset for Cupertino widgets based on Apple styled icons', + repository: 'https://github.com/flutter/packages/tree/main/third_party/packages/cupertino_icons', + authors: [], + version: '1.0.8', + license: '''The MIT License (MIT) + +Copyright (c) 2016 Vladimir Kharlampidi + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'dart_pubspec_licenses', + description: 'A library to make it easy to extract OSS license information from Dart packages using pubspec.yaml', + homepage: 'https://github.com/espresso3389/flutter_oss_licenses/tree/master/packages/dart_pubspec_licenses', + repository: 'https://github.com/espresso3389/flutter_oss_licenses', + authors: [], + version: '2.0.3', + license: '''MIT License + +Copyright (c) 2019 Takashi Kawasaki + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'dbus', + description: 'A native Dart implementation of the D-Bus message bus client. This package allows Dart applications to directly access services on the Linux desktop.', + homepage: 'https://github.com/canonical/dbus.dart', + authors: [], + version: '0.7.11', + license: '''Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'dio', + description: '''A powerful HTTP networking package, +supports Interceptors, +Aborting and canceling a request, +Custom adapters, Transformers, etc. +''', + homepage: 'https://github.com/cfug/dio', + repository: 'https://github.com/cfug/dio/blob/main/dio', + authors: [], + version: '5.9.0', + license: '''MIT License + +Copyright (c) 2018 Wen Du (wendux) +Copyright (c) 2022 The CFUG Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'dio_web_adapter', + description: 'An adapter that supports Dio on Web.', + homepage: 'https://github.com/cfug/dio', + repository: 'https://github.com/cfug/dio/blob/main/plugins/web_adapter', + authors: [], + version: '2.1.1', + license: '''MIT License + +Copyright (c) 2018 Wen Du (wendux) +Copyright (c) 2022 The CFUG Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'fake_async', + description: 'Fake asynchronous events such as timers and microtasks for deterministic testing.', + repository: 'https://github.com/dart-lang/test/tree/master/pkgs/fake_async', + authors: [], + version: '1.3.3', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'ffi', + description: 'Utilities for working with Foreign Function Interface (FFI) code.', + repository: 'https://github.com/dart-lang/native/tree/main/pkgs/ffi', + authors: [], + version: '2.1.4', + license: '''Copyright 2019, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'file', + description: 'A pluggable, mockable file system abstraction for Dart. Supports local file system access, as well as in-memory file systems, record-replay file systems, and chroot file systems.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/file', + authors: [], + version: '7.0.1', + license: '''Copyright 2017, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'file_picker', + description: 'A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support.', + homepage: 'https://github.com/miguelpruivo/plugins_flutter_file_picker', + repository: 'https://github.com/miguelpruivo/flutter_file_picker', + authors: [], + version: '10.3.1', + license: '''MIT License + +Copyright (c) 2018 Miguel Ruivo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'file_saver', + description: 'This package will help you save file with a single method on any platform including macOS, iOS, Android, Windows, Web, Linux.', + homepage: 'https://hassanansari.dev', + repository: 'https://github.com/incrediblezayed/file_saver', + authors: [], + version: '0.2.14', + license: '''BSD 3-Clause License + +Copyright (c) 2021, Hassan Ansari +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'fixnum', + description: 'Library for 32- and 64-bit signed fixed-width integers with consistent behavior between native and JS runtimes.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/fixnum', + authors: [], + version: '1.1.1', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'flutter', + description: 'A framework for writing Flutter applications', + homepage: 'https://flutter.dev', + authors: [], + version: '3.32.1', + license: '''Copyright 2014 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: true, + isDirectDependency: true, + ), + Package( + name: 'flutter_keyboard_visibility', + description: 'Flutter plugin for discovering the state of the soft-keyboard visibility on Android and iOS.', + homepage: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + repository: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + authors: [], + version: '6.0.0', + license: '''The MIT License + +Copyright (c) 2022 Jason Rai +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'flutter_keyboard_visibility_linux', + description: "An implementation for the linux platform of `flutter_keyboard_visibility'", + homepage: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + repository: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + authors: [], + version: '1.0.0', + license: '''The MIT License + +Copyright (c) 2022 Jason Rai +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'flutter_keyboard_visibility_macos', + description: "An implementation for the macOS platform of `flutter_keyboard_visibility'", + homepage: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + repository: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + authors: [], + version: '1.0.0', + license: '''The MIT License + +Copyright (c) 2022 Jason Rai +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'flutter_keyboard_visibility_platform_interface', + description: 'A common platform interface for the flutter_keyboard_visibility plugin.', + homepage: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + repository: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + authors: [], + version: '2.0.0', + license: '''The MIT License + +Copyright (c) 2006-2020 +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'flutter_keyboard_visibility_web', + description: "An implementation for the web platform of `flutter_keyboard_visibility'", + homepage: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + repository: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + authors: [], + version: '2.0.0', + license: '''The MIT License + +Copyright (c) 2006-2020 +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'flutter_keyboard_visibility_windows', + description: "An implementation for the Windows platform of `flutter_keyboard_visibility'", + homepage: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + repository: 'https://github.com/MisterJimson/flutter_keyboard_visibility', + authors: [], + version: '1.0.0', + license: '''The MIT License + +Copyright (c) 2022 Jason Rai +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'flutter_lints', + description: 'Recommended lints for Flutter apps, packages, and plugins to encourage good coding practices.', + repository: 'https://github.com/flutter/packages/tree/main/packages/flutter_lints', + authors: [], + version: '5.0.0', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'flutter_oss_licenses', + description: 'A tool to generate detail and better OSS license list using pubspec.yaml/lock files.', + homepage: 'https://github.com/espresso3389/flutter_oss_licenses/tree/master/packages/flutter_oss_licenses', + repository: 'https://github.com/espresso3389/flutter_oss_licenses', + authors: [], + version: '2.0.3', + license: '''MIT License + +Copyright (c) 2019 Takashi Kawasaki + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'flutter_plugin_android_lifecycle', + description: 'Flutter plugin for accessing an Android Lifecycle within other plugins.', + repository: 'https://github.com/flutter/packages/tree/main/packages/flutter_plugin_android_lifecycle', + authors: [], + version: '2.0.29', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'flutter_rating_bar', + description: 'A simple yet fully customizable ratingbar for flutter which also include a rating bar indicator, supporting any fraction of rating.', + homepage: 'https://sarbagyastha.com.np', + repository: 'https://github.com/sarbagyastha/flutter_rating_bar', + authors: [], + version: '4.0.1', + license: '''The MIT License (MIT) + +Copyright (c) 2021 Sarbagya Dhaubanjar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'font_awesome_flutter', + description: 'The Font Awesome Icon pack available as Flutter Icons. Provides 2000 additional icons to use in your apps.', + repository: 'https://github.com/fluttercommunity/font_awesome_flutter', + authors: [], + version: '10.9.1', + license: '''MIT License + +Copyright (c) 2017 Brian Egan +Copyright (c) 2020 Michael Spiss +Font Awesome Icons by @fontawesome - https://fontawesome.com +License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'frontend_server_client', + description: 'Client code to start and interact with the frontend_server compiler from the Dart SDK.', + repository: 'https://github.com/dart-lang/webdev/tree/master/frontend_server_client', + authors: [], + version: '4.0.0', + license: '''Copyright 2020, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'glob', + description: 'A library to perform Bash-style file and directory globbing.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/glob', + authors: [], + version: '2.1.3', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'google_fonts', + description: 'A Flutter package to use fonts from fonts.google.com. Supports HTTP fetching, caching, and asset bundling.', + repository: 'https://github.com/material-foundation/flutter-packages/tree/main/packages/google_fonts', + authors: [], + version: '6.3.0', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'http', + description: 'A composable, multi-platform, Future-based API for HTTP requests.', + repository: 'https://github.com/dart-lang/http/tree/master/pkgs/http', + authors: [], + version: '1.5.0', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'http_multi_server', + description: 'A dart:io HttpServer wrapper that handles requests from multiple servers.', + repository: 'https://github.com/dart-lang/http/tree/master/pkgs/http_multi_server', + authors: [], + version: '3.2.2', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'http_parser', + description: 'A platform-independent package for parsing and serializing HTTP formats.', + repository: 'https://github.com/dart-lang/http/tree/master/pkgs/http_parser', + authors: [], + version: '4.1.2', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'intl', + description: 'Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.', + repository: 'https://github.com/dart-lang/i18n/tree/main/pkgs/intl', + authors: [], + version: '0.20.2', + license: '''Copyright 2013, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'io', + description: 'Utilities for the Dart VM Runtime including support for ANSI colors, file copying, and standard exit code values.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/io', + authors: [], + version: '1.0.5', + license: '''Copyright 2017, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'js', + description: 'Annotations to create static Dart interfaces for JavaScript APIs.', + repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/js', + authors: [], + version: '0.7.2', + license: '''Copyright 2012, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'json_annotation', + description: 'Classes and helper functions that support JSON code generation via the `json_serializable` package.', + repository: 'https://github.com/google/json_serializable.dart/tree/master/json_annotation', + authors: [], + version: '4.9.0', + license: '''Copyright 2017, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'json_schema', + description: 'JSON Schema implementation in Dart', + homepage: 'https://github.com/workiva/json_schema', + authors: [], + version: '5.2.1', + license: '''Copyright 2013-2022 Workiva Inc. + +Licensed under the Boost Software License (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.boost.org/LICENSE_1_0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +This software or document includes material copied from or derived +from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +Copyright (c) 2012 Julian Berman, which is licensed under the following terms: + + Copyright (c) 2012 Julian Berman + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'leak_tracker', + description: 'A framework for memory leak tracking for Dart and Flutter applications.', + repository: 'https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker', + authors: [], + version: '10.0.9', + license: '''Copyright 2022, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'leak_tracker_flutter_testing', + description: 'An internal package to test leak tracking with Flutter.', + repository: 'https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker_flutter_testing', + authors: [], + version: '3.0.9', + license: '''Copyright 2022, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'leak_tracker_testing', + description: 'Leak tracking code intended for usage in tests.', + repository: 'https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker_testing', + authors: [], + version: '3.0.1', + license: '''Copyright 2022, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'lints', + description: """Official Dart lint rules. Defines the 'core' and 'recommended' set of lints suggested by the Dart team. +""", + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/lints', + authors: [], + version: '5.1.1', + license: '''Copyright 2021, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'logger', + description: 'Small, easy to use and extensible logger which prints beautiful logs.', + repository: 'https://github.com/SourceHorizon/logger', + authors: [], + version: '2.6.1', + license: '''MIT License + +Copyright (c) 2019 Simon Leier +Copyright (c) 2019 Harm Aarts +Copyright (c) 2023 Severin Hamader + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'logging', + description: 'Provides APIs for debugging and error logging, similar to loggers in other languages, such as the Closure JS Logger and java.util.logging.Logger.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/logging', + authors: [], + version: '1.3.0', + license: '''Copyright 2013, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'matcher', + description: 'Support for specifying test expectations via an extensible Matcher class. Also includes a number of built-in Matcher implementations for common cases.', + repository: 'https://github.com/dart-lang/test/tree/master/pkgs/matcher', + authors: [], + version: '0.12.17', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'material_color_utilities', + description: 'Algorithms and utilities that power the Material Design 3 color system, including choosing theme colors from images and creating tones of colors; all in a new color space.', + repository: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart', + authors: [], + version: '0.11.1', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'meta', + description: "Annotations used to express developer intentions that can't otherwise be deduced by statically analyzing source code.", + repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/meta', + authors: [], + version: '1.16.0', + license: '''Copyright 2016, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'mime', + description: 'Utilities for handling media (MIME) types, including determining a type from a file extension and file contents.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/mime', + authors: [], + version: '2.0.0', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'node_preamble', + description: 'Better node.js preamble for dart2js, use it in your build system.', + homepage: 'https://github.com/mbullington/node_preamble.dart', + authors: ['Michael Bullington '], + version: '2.0.2', + license: '''The MIT License (MIT) + +Copyright (c) 2015 Michael Bullington + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +=== + +Copyright 2012, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'package_config', + description: 'Support for reading and writing Dart Package Configuration files.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/package_config', + authors: [], + version: '2.2.0', + license: '''Copyright 2019, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'package_info_plus', + description: 'Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android.', + homepage: 'https://github.com/fluttercommunity/plus_plugins', + repository: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus/package_info_plus', + authors: [], + version: '8.3.1', + license: '''Copyright 2017 The Chromium Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'package_info_plus_platform_interface', + description: 'A common platform interface for the package_info_plus plugin.', + homepage: 'https://github.com/fluttercommunity/plus_plugins', + repository: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/', + authors: [], + version: '3.2.1', + license: '''Copyright 2017 The Chromium Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'path', + description: 'A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/path', + authors: [], + version: '1.9.1', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'path_provider', + description: 'Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories.', + repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider', + authors: [], + version: '2.1.5', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'path_provider_android', + description: 'Android implementation of the path_provider plugin.', + repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_android', + authors: [], + version: '2.2.17', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'path_provider_foundation', + description: 'iOS and macOS implementation of the path_provider plugin', + repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_foundation', + authors: [], + version: '2.4.2', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'path_provider_linux', + description: 'Linux implementation of the path_provider plugin', + repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_linux', + authors: [], + version: '2.2.1', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'path_provider_platform_interface', + description: 'A common platform interface for the path_provider plugin.', + repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_platform_interface', + authors: [], + version: '2.1.2', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'path_provider_windows', + description: 'Windows implementation of the path_provider plugin', + repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_windows', + authors: [], + version: '2.3.0', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'petitparser', + description: 'A dynamic parser framework to build efficient grammars and parsers quickly.', + homepage: 'https://petitparser.github.io', + repository: 'https://github.com/petitparser/dart-petitparser', + authors: [], + version: '7.0.1', + license: '''The MIT License + +Copyright (c) 2006-2024 Lukas Renggli. +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'platform', + description: 'A pluggable, mockable platform information abstraction for Dart.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/platform', + authors: [], + version: '3.1.6', + license: '''Copyright 2017, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'plugin_platform_interface', + description: 'Reusable base class for platform interfaces of Flutter federated plugins, to help enforce best practices.', + repository: 'https://github.com/flutter/packages/tree/main/packages/plugin_platform_interface', + authors: [], + version: '2.1.8', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'pool', + description: 'Manage a finite pool of resources. Useful for controlling concurrent file system or network requests.', + repository: 'https://github.com/dart-lang/pool', + authors: [], + version: '1.5.1', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'pub_semver', + description: "Versions and version constraints implementing pub's versioning policy. This is very similar to vanilla semver, with a few corner cases.", + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/pub_semver', + authors: [], + version: '2.2.0', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'quiver', + description: 'Quiver is a set of utility libraries for Dart that makes using many Dart libraries easier and more convenient, or adds additional functionality.', + repository: 'https://github.com/google/quiver-dart', + authors: [], + version: '3.2.2', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'rate_my_app', + description: 'Allows to kindly ask users to rate your app if custom conditions are met (eg. install time, number of launches, etc...).', + homepage: 'https://github.com/Skyost/RateMyApp', + authors: [], + version: '2.3.2', + license: '''MIT License + +Copyright (c) 2019 Hugo DELAUNAY "Skyost" + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'reorderables', + description: 'Reorderable table, row, column, wrap, sliver list that allow drag and drop of their children.', + homepage: 'https://github.com/hanshengchiu/reorderables', + authors: [], + version: '0.4.4', + license: '''MIT License + +Copyright (c) 2019 Hansheng Chiu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'rfc_6901', + description: 'JSON Pointer (RFC 6901). Reads/writes referred values in JSON documents.', + homepage: 'https://github.com/f3ath/rfc-6901-dart', + authors: [], + version: '0.2.0', + license: '''MIT License + +Copyright (c) 2021 The Конь + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'shared_preferences', + description: 'Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android.', + repository: 'https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences', + authors: [], + version: '2.5.3', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'shared_preferences_android', + description: 'Android implementation of the shared_preferences plugin', + repository: 'https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_android', + authors: [], + version: '2.4.11', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'shared_preferences_foundation', + description: 'iOS and macOS implementation of the shared_preferences plugin.', + repository: 'https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_foundation', + authors: [], + version: '2.5.4', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'shared_preferences_linux', + description: 'Linux implementation of the shared_preferences plugin', + repository: 'https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_linux', + authors: [], + version: '2.4.1', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'shared_preferences_platform_interface', + description: 'A common platform interface for the shared_preferences plugin.', + repository: 'https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_platform_interface', + authors: [], + version: '2.4.1', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'shared_preferences_web', + description: 'Web platform implementation of shared_preferences', + repository: 'https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_web', + authors: [], + version: '2.4.3', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'shared_preferences_windows', + description: 'Windows implementation of shared_preferences', + repository: 'https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_windows', + authors: [], + version: '2.4.1', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'shelf', + description: '''A model for web server middleware that encourages composition and easy reuse. +''', + repository: 'https://github.com/dart-lang/shelf/tree/master/pkgs/shelf', + authors: [], + version: '1.4.2', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'shelf_packages_handler', + description: 'A shelf handler for serving a `packages/` directory.', + repository: 'https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_packages_handler', + authors: [], + version: '3.0.2', + license: '''Copyright 2016, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'shelf_static', + description: 'Static file server support for the shelf package and ecosystem.', + repository: 'https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_static', + authors: [], + version: '1.1.3', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'shelf_web_socket', + description: 'A shelf handler that wires up a listener for every connection.', + repository: 'https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_web_socket', + authors: [], + version: '3.0.0', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'source_map_stack_trace', + description: 'A package for applying source maps to stack traces.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/source_map_stack_trace', + authors: [], + version: '2.1.2', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'source_maps', + description: 'A library to programmatically manipulate source map files.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/source_maps', + authors: [], + version: '0.10.13', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'source_span', + description: 'Provides a standard representation for source code locations and spans.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/source_span', + authors: [], + version: '1.10.1', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'sprintf', + description: 'Dart implementation of sprintf. Provides simple printf like formatting such as sprintf("hello %s", ["world"]);', + homepage: 'https://github.com/Naddiseo/dart-sprintf', + authors: [], + version: '7.0.0', + license: '''Copyright (c) 2012, Richard Eames +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'stack_trace', + description: 'A package for manipulating stack traces and printing them readably.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/stack_trace', + authors: [], + version: '1.12.1', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'stream_channel', + description: 'An abstraction for two-way communication channels based on the Dart Stream class.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/stream_channel', + authors: [], + version: '2.1.4', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'string_scanner', + description: 'A class for parsing strings using a sequence of patterns.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/string_scanner', + authors: [], + version: '1.4.1', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'syncfusion_flutter_charts', + description: 'A Flutter Charts library which includes data visualization widgets such as cartesian and circular charts, to create real-time, interactive, high-performance, animated charts.', + homepage: 'https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_charts', + authors: [], + version: '30.2.5', + license: '''Syncfusion® License + +Syncfusion® Flutter Chart package is available under the Syncfusion Essential Studio® program, and can be licensed either under the Syncfusion® Community License Program or the Syncfusion® commercial license. + +To be qualified for the Syncfusion® Community License Program you must have a gross revenue of less than one (1) million U.S. dollars (\$1,000,000.00 USD) per year and have less than five (5) developers in your organization, and agree to be bound by Syncfusion® terms and conditions. + +Customers who do not qualify for the community license can contact sales@syncfusion.com for commercial licensing options. + +Under no circumstances can you use this product without (1) either a Community License or a commercial license and (2) without agreeing and abiding by Syncfusion® license containing all terms and conditions. + +The Syncfusion® license that contains the terms and conditions can be found at +https://www.syncfusion.com/content/downloads/syncfusion_license.pdf''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'syncfusion_flutter_core', + description: 'Syncfusion Flutter Core is a dependent package for all the Syncfusion Flutter widgets.', + homepage: 'https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_core', + authors: [], + version: '30.2.5', + license: '''Syncfusion® License + +Syncfusion® Flutter Core package is available under the Syncfusion Essential Studio® program, and can be licensed either under the Syncfusion® Community License Program or the Syncfusion® commercial license. + +To be qualified for the Syncfusion® Community License Program you must have a gross revenue of less than one (1) million U.S. dollars (\$1,000,000.00 USD) per year and have less than five (5) developers in your organization, and agree to be bound by Syncfusion® terms and conditions. + +Customers who do not qualify for the community license can contact sales@syncfusion.com for commercial licensing options. + +Under no circumstances can you use this product without (1) either a Community License or a commercial license and (2) without agreeing and abiding by Syncfusion® license containing all terms and conditions. + +The Syncfusion® license that contains the terms and conditions can be found at +https://www.syncfusion.com/content/downloads/syncfusion_license.pdf''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'term_glyph', + description: 'Useful Unicode glyphs and ASCII substitutes.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/term_glyph', + authors: [], + version: '1.2.2', + license: '''Copyright 2017, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'test', + description: 'A full featured library for writing and running Dart tests across platforms.', + repository: 'https://github.com/dart-lang/test/tree/master/pkgs/test', + authors: [], + version: '1.25.15', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'test_api', + description: 'The user facing API for structuring Dart tests and checking expectations.', + repository: 'https://github.com/dart-lang/test/tree/master/pkgs/test_api', + authors: [], + version: '0.7.4', + license: '''Copyright 2018, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'test_core', + description: 'A basic library for writing tests and running them on the VM.', + repository: 'https://github.com/dart-lang/test/tree/master/pkgs/test_core', + authors: [], + version: '0.6.8', + license: '''Copyright 2018, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'typed_data', + description: 'Utility functions and classes related to the dart:typed_data library.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/typed_data', + authors: [], + version: '1.4.0', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'uri', + description: 'Utilities for building and parsing URIs, including support for parsing URI templates as defined in RFC 6570.', + repository: 'https://github.com/google/uri.dart', + authors: [], + version: '1.0.0', + license: '''Copyright 2013, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'url_launcher', + description: 'Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes.', + repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher', + authors: [], + version: '6.3.2', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'url_launcher_android', + description: 'Android implementation of the url_launcher plugin.', + repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_android', + authors: [], + version: '6.3.17', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'url_launcher_ios', + description: 'iOS implementation of the url_launcher plugin.', + repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_ios', + authors: [], + version: '6.3.4', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'url_launcher_linux', + description: 'Linux implementation of the url_launcher plugin.', + repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_linux', + authors: [], + version: '3.2.1', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'url_launcher_macos', + description: 'macOS implementation of the url_launcher plugin.', + repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_macos', + authors: [], + version: '3.2.3', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'url_launcher_platform_interface', + description: 'A common platform interface for the url_launcher plugin.', + repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_platform_interface', + authors: [], + version: '2.3.2', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'url_launcher_web', + description: 'Web platform implementation of url_launcher', + repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_web', + authors: [], + version: '2.4.1', + license: '''url_launcher_web + +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +platform_detect + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 Workiva Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'url_launcher_windows', + description: 'Windows implementation of the url_launcher plugin.', + repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_windows', + authors: [], + version: '3.1.4', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'uuid', + description: '''RFC4122 (v1, v4, v5, v6, v7, v8) UUID Generator and Parser for Dart +''', + repository: 'https://github.com/Daegalus/dart-uuid', + authors: [], + version: '4.5.1', + license: '''Copyright (c) 2021 Yulian Kuncheff + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: true, + ), + Package( + name: 'vector_math', + description: 'A Vector Math library for 2D and 3D applications.', + repository: 'https://github.com/google/vector_math.dart', + authors: [], + version: '2.1.4', + license: '''Copyright 2015, Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2013 Andrew Magill + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'vm_service', + description: 'A library to communicate with a service implementing the Dart VM service protocol.', + repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/vm_service', + authors: [], + version: '15.0.0', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'watcher', + description: 'A file system watcher. It monitors changes to contents of directories and sends notifications when files have been added, removed, or modified.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/watcher', + authors: [], + version: '1.1.2', + license: '''Copyright 2014, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'web', + description: 'Lightweight browser API bindings built around JS interop.', + repository: 'https://github.com/dart-lang/web', + authors: [], + version: '1.1.1', + license: '''Copyright 2023, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'web_socket', + description: 'Any easy-to-use library for communicating with WebSockets that has multiple implementations.', + repository: 'https://github.com/dart-lang/http/tree/master/pkgs/web_socket', + authors: [], + version: '1.0.1', + license: '''Copyright 2024, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'web_socket_channel', + description: 'StreamChannel wrappers for WebSockets. Provides a cross-platform WebSocketChannel API, a cross-platform implementation of that API that communicates over an underlying StreamChannel.', + repository: 'https://github.com/dart-lang/http/tree/master/pkgs/web_socket_channel', + authors: [], + version: '3.0.3', + license: '''Copyright 2016, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'webkit_inspection_protocol', + description: '''A client for the Chrome DevTools Protocol (previously called the Webkit Inspection Protocol). +''', + repository: 'https://github.com/google/webkit_inspection_protocol.dart', + authors: [], + version: '1.2.1', + license: '''Copyright 2013, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'win32', + description: '''Access common Win32 APIs directly from Dart using FFI — no C required! +''', + homepage: 'https://win32.pub', + repository: 'https://github.com/halildurmus/win32', + authors: [], + version: '5.14.0', + license: '''BSD 3-Clause License + +Copyright (c) 2024, Halil Durmus + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'xdg_directories', + description: 'A Dart package for reading XDG directory configuration information on Linux.', + repository: 'https://github.com/flutter/packages/tree/main/packages/xdg_directories', + authors: [], + version: '1.1.0', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'xml', + description: 'A lightweight library for parsing, traversing, querying, transforming and building XML documents.', + homepage: 'https://github.com/renggli/dart-xml', + authors: [], + version: '6.6.1', + license: '''The MIT License + +Copyright (c) 2006-2025 Lukas Renggli. +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + Package( + name: 'yaml', + description: 'A parser for YAML, a human-friendly data serialization standard', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/yaml', + authors: [], + version: '3.1.3', + license: '''Copyright (c) 2014, the Dart project authors. +Copyright (c) 2006, Kirill Simonov. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + isDirectDependency: false, + ), + +]; + +/// Package license definition. +class Package { + /// Package name + final String name; + /// Description + final String description; + /// Website URL + final String? homepage; + /// Repository URL + final String? repository; + /// Authors + final List authors; + /// Version + final String version; + /// License + final String? license; + /// Whether the license is in markdown format or not (plain text). + final bool isMarkdown; + /// Whether the package is included in the SDK or not. + final bool isSdk; + /// Whether the package is direct dependency or not. + final bool isDirectDependency; + + const Package({ + required this.name, + required this.description, + this.homepage, + this.repository, + required this.authors, + required this.version, + this.license, + required this.isMarkdown, + required this.isSdk, + required this.isDirectDependency, + }); +} diff --git a/lib/presentation/views/home/active_game/active_game_view.dart b/lib/presentation/views/home/active_game/active_game_view.dart new file mode 100644 index 0000000..3a4c0e5 --- /dev/null +++ b/lib/presentation/views/home/active_game/active_game_view.dart @@ -0,0 +1,552 @@ +import 'package:cabo_counter/core/constants.dart'; +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/data/game_manager.dart'; +import 'package:cabo_counter/data/game_session.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/home/active_game/graph_view.dart'; +import 'package:cabo_counter/presentation/views/home/active_game/mode_selection_view.dart'; +import 'package:cabo_counter/presentation/views/home/active_game/points_view.dart'; +import 'package:cabo_counter/presentation/views/home/active_game/round_view.dart'; +import 'package:cabo_counter/presentation/views/home/create_game_view.dart'; +import 'package:cabo_counter/services/config_service.dart'; +import 'package:cabo_counter/services/local_storage_service.dart'; +import 'package:collection/collection.dart'; +import 'package:confetti/confetti.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +/// Displays the active game view, showing game details, player rankings, rounds, and statistics. +/// +/// This view allows users to interact with an ongoing game session, including viewing player scores, +/// navigating through rounds, ending or deleting the game, exporting game data, and starting a new game +/// with the same settings. It also provides visual feedback such as confetti animation when the game ends. +/// +/// The widget listens to changes in the provided [GameSession] and updates the UI accordingly. +class ActiveGameView extends StatefulWidget { + final GameSession gameSession; + const ActiveGameView({super.key, required this.gameSession}); + + @override + // ignore: library_private_types_in_public_api + _ActiveGameViewState createState() => _ActiveGameViewState(); +} + +class _ActiveGameViewState extends State { + /// Constant value to represent a press on the cancel button in round view. + static const int kRoundCancelled = -1; + + final confettiController = ConfettiController( + duration: const Duration(seconds: 10), + ); + + late final GameSession gameSession; + + /// A list of the ranks for each player corresponding to their index in sortedPlayerIndices + late List denseRanks; + + /// A list of player indices sorted by their scores in ascending order. + late List sortedPlayerIndices; + + @override + void initState() { + super.initState(); + gameSession = widget.gameSession; + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + ListenableBuilder( + listenable: gameSession, + builder: (context, _) { + sortedPlayerIndices = _getSortedPlayerIndices(); + denseRanks = _calculateDenseRank( + gameSession.playerScores, sortedPlayerIndices); + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + previousPageTitle: AppLocalizations.of(context).games, + middle: Text(AppLocalizations.of(context).overview), + ), + child: SafeArea( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), + child: Text( + AppLocalizations.of(context).game, + style: CustomTheme.rowTitle, + ), + ), + CupertinoListTile( + title: Text(AppLocalizations.of(context).name), + trailing: Text( + gameSession.gameTitle, + style: TextStyle(color: CustomTheme.primaryColor), + ), + ), + CupertinoListTile( + title: Text(AppLocalizations.of(context).mode), + trailing: Text( + gameSession.isPointsLimitEnabled + ? '${ConfigService.getPointLimit()} ${AppLocalizations.of(context).points}' + : AppLocalizations.of(context).unlimited, + style: TextStyle(color: CustomTheme.primaryColor), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).players, + style: CustomTheme.rowTitle, + ), + ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: gameSession.players.length, + itemBuilder: (BuildContext context, int index) { + int playerIndex = sortedPlayerIndices[index]; + return CupertinoListTile( + padding: + const EdgeInsets.fromLTRB(14, 5, 14, 0), + title: Row( + children: [ + _getPlacementTextWidget(index), + const SizedBox(width: 5), + Text( + gameSession.players[playerIndex], + style: const TextStyle( + fontWeight: FontWeight.bold), + ), + ], + ), + trailing: Row( + children: [ + const SizedBox(width: 5), + Text( + '${gameSession.playerScores[playerIndex]} ' + '${AppLocalizations.of(context).points}') + ], + ), + ); + }, + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).rounds, + style: CustomTheme.rowTitle, + ), + ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: gameSession.roundNumber, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.all(1), + child: CupertinoListTile( + backgroundColorActivated: + CustomTheme.backgroundColor, + title: Text( + '${AppLocalizations.of(context).round} ${index + 1}', + ), + trailing: index + 1 != + gameSession.roundNumber || + gameSession.isGameFinished == true + ? (const Text('\u{2705}', + style: TextStyle(fontSize: 22))) + : const Text('\u{23F3}', + style: TextStyle(fontSize: 22)), + onTap: () async { + _openRoundView(context, index + 1); + }, + )); + }, + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).statistics, + style: CustomTheme.rowTitle, + ), + ), + Column( + children: [ + CupertinoListTile( + title: Text( + AppLocalizations.of(context) + .scoring_history, + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => GraphView( + gameSession: gameSession, + )))), + CupertinoListTile( + title: Text( + AppLocalizations.of(context).point_overview, + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => PointsView( + gameSession: gameSession, + )))), + ], + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).game, + style: CustomTheme.rowTitle, + ), + ), + Column( + children: [ + Visibility( + visible: !gameSession.isPointsLimitEnabled, + child: CupertinoListTile( + title: Text( + AppLocalizations.of(context).end_game, + style: gameSession.roundNumber > 1 && + !gameSession.isGameFinished + ? const TextStyle(color: Colors.white) + : const TextStyle( + color: Colors.white30), + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () { + if (gameSession.roundNumber > 1 && + !gameSession.isGameFinished) { + _showEndGameDialog(); + } + }), + ), + CupertinoListTile( + title: Text( + AppLocalizations.of(context).delete_game, + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () { + _showDeleteGameDialog().then((value) { + if (value) { + _removeGameSession(gameSession); + } + }); + }, + ), + CupertinoListTile( + title: Text( + AppLocalizations.of(context) + .new_game_same_settings, + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => CreateGameView( + gameTitle: + gameSession.gameTitle, + gameMode: widget.gameSession + .isPointsLimitEnabled == + true + ? GameMode.pointLimit + : GameMode.unlimited, + players: gameSession.players, + ))); + }, + ), + CupertinoListTile( + title: Text( + AppLocalizations.of(context).export_game, + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () async { + final success = await LocalStorageService + .exportSingleGameSession( + widget.gameSession); + if (!success && context.mounted) { + showCupertinoDialog( + context: context, + builder: (context) => + CupertinoAlertDialog( + title: Text( + AppLocalizations.of(context) + .export_error_title), + content: Text( + AppLocalizations.of(context) + .export_error_message), + actions: [ + CupertinoDialogAction( + child: Text( + AppLocalizations.of(context) + .ok), + onPressed: () => + Navigator.pop(context), + ), + ], + ), + ); + } + }), + ], + ) + ], + ), + ), + )); + }), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Center( + child: ConfettiWidget( + blastDirectionality: BlastDirectionality.explosive, + particleDrag: 0.07, + emissionFrequency: 0.1, + numberOfParticles: 10, + minBlastForce: 5, + maxBlastForce: 20, + confettiController: confettiController, + ), + ), + ], + ), + ], + ); + } + + /// Shows a dialog to confirm ending the game. + /// If the user confirms, it calls the `endGame` method on the game manager + void _showEndGameDialog() { + showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).end_game_title), + content: Text(AppLocalizations.of(context).end_game_message), + actions: [ + CupertinoDialogAction( + isDestructiveAction: true, + child: Text( + AppLocalizations.of(context).end_game, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + onPressed: () { + setState(() { + gameManager.endGame(gameSession.id); + _playFinishAnimation(context); + }); + Navigator.pop(context); + }, + ), + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).cancel), + onPressed: () => Navigator.pop(context), + ), + ], + ); + }, + ); + } + + /// Returns a list of player indices sorted by their scores in + /// ascending order. + List _getSortedPlayerIndices() { + List playerIndices = + List.generate(gameSession.players.length, (index) => index); + // Sort the indices based on the summed points + playerIndices.sort((a, b) { + int scoreA = gameSession.playerScores[a]; + int scoreB = gameSession.playerScores[b]; + if (scoreA != scoreB) { + return scoreA.compareTo(scoreB); + } + return a.compareTo(b); + }); + return playerIndices; + } + + /// Calculates the dense rank for a player based on their index in the sorted list of players. + List _calculateDenseRank( + List playerScores, List sortedIndices) { + List denseRanks = []; + int rank = 1; + for (int i = 0; i < sortedIndices.length; i++) { + if (i > 0) { + int prevScore = playerScores[sortedIndices[i - 1]]; + int currScore = playerScores[sortedIndices[i]]; + if (currScore != prevScore) { + rank++; + } + } + denseRanks.add(rank); + } + return denseRanks; + } + + /// Returns a text widget representing the placement text based on the given placement number. + /// [index] is the index of the player in [players] list, + Text _getPlacementTextWidget(int index) { + int placement = denseRanks[index]; + switch (placement) { + case 1: + return const Text('\u{1F947}', style: TextStyle(fontSize: 22)); // 🥇 + case 2: + return const Text('\u{1F948}', style: TextStyle(fontSize: 22)); // 🥈 + case 3: + return const Text('\u{1F949}', style: TextStyle(fontSize: 22)); // 🥉 + default: + return Text(' $placement.', + style: const TextStyle(fontWeight: FontWeight.bold)); + } + } + + /// Shows a dialog to confirm deleting the game session. + Future _showDeleteGameDialog() async { + return await showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).delete_game_title), + content: Text( + AppLocalizations.of(context) + .delete_game_message(gameSession.gameTitle), + ), + actions: [ + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).cancel), + onPressed: () => Navigator.pop(context, false), + ), + CupertinoDialogAction( + child: Text( + AppLocalizations.of(context).delete, + style: const TextStyle( + fontWeight: FontWeight.bold, color: Colors.red), + ), + onPressed: () { + Navigator.pop(context, true); + }, + ), + ], + ); + }, + ) ?? + false; + } + + /// Removes the game session in the game manager and navigates back to the previous screen. + /// If the game session does not exist in the game list, it shows an error dialog. + Future _removeGameSession(GameSession gameSession) async { + if (gameManager.gameExistsInGameList(gameSession.id)) { + Navigator.pop(context); + + WidgetsBinding.instance.addPostFrameCallback((_) { + gameManager.removeGameSessionById(gameSession.id); + }); + } else { + showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).id_error_title), + content: Text(AppLocalizations.of(context).id_error_message), + actions: [ + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).ok), + onPressed: () => Navigator.pop(context), + ), + ], + ); + }); + } + } + + /// Recursively opens the RoundView for the specified round number. + /// It starts with the given [roundNumber] and continues to open the next round + /// until the user navigates back or the round number is invalid. + void _openRoundView(BuildContext context, int roundNumber) async { + final round = await Navigator.of(context, rootNavigator: true).push( + CupertinoPageRoute( + fullscreenDialog: true, + builder: (context) => RoundView( + gameSession: gameSession, + roundNumber: roundNumber, + ), + ), + ); + + // If the user presses the cancel button + if (round == kRoundCancelled) return; + + if (widget.gameSession.isGameFinished && context.mounted) { + _playFinishAnimation(context); + } + + // If the previous round was not the last one + if (round != null && round >= 0) { + WidgetsBinding.instance.addPostFrameCallback((_) async { + await Future.delayed( + const Duration(milliseconds: Constants.kRoundViewDelay)); + if (context.mounted) { + _openRoundView(context, round); + } + }); + } + } + + /// Plays the confetti animation and shows a dialog with the winner's information. + Future _playFinishAnimation(BuildContext context) async { + String winner = widget.gameSession.winner; + int winnerPoints = widget.gameSession.playerScores.min; + int winnerAmount = winner.contains('&') ? 2 : 1; + + confettiController.play(); + + await Future.delayed(const Duration(milliseconds: Constants.kPopUpDelay)); + + if (context.mounted) { + showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).end_of_game_title), + content: Text(AppLocalizations.of(context) + .end_of_game_message(winnerAmount, winner, winnerPoints)), + actions: [ + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).ok), + onPressed: () { + confettiController.stop(); + Navigator.pop(context); + }, + ), + ], + ); + }); + } + } + + @override + void dispose() { + confettiController.dispose(); + super.dispose(); + } +} diff --git a/lib/presentation/views/home/active_game/graph_view.dart b/lib/presentation/views/home/active_game/graph_view.dart new file mode 100644 index 0000000..b7251d6 --- /dev/null +++ b/lib/presentation/views/home/active_game/graph_view.dart @@ -0,0 +1,139 @@ +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/data/game_session.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:syncfusion_flutter_charts/charts.dart'; + +/// A widget that displays the cumulative scoring history of a game session as a line graph. +/// +/// The [GraphView] visualizes the progression of each player's score over multiple rounds +/// using a line chart. It supports dynamic coloring for each player, axis formatting, +/// and handles cases where insufficient data is available to render the graph. +class GraphView extends StatefulWidget { + final GameSession gameSession; + + const GraphView({super.key, required this.gameSession}); + + @override + State createState() => _GraphViewState(); +} + +class _GraphViewState extends State { + /// List of colors for the graph lines. + final List lineColors = [ + CustomTheme.graphColor1, + CustomTheme.graphColor2, + CustomTheme.graphColor3, + CustomTheme.graphColor4, + CustomTheme.graphColor5 + ]; + + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text(AppLocalizations.of(context).scoring_history), + previousPageTitle: AppLocalizations.of(context).overview, + ), + child: SafeArea( + child: Visibility( + visible: widget.gameSession.roundNumber > 1 || + widget.gameSession.isGameFinished, + replacement: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Center( + child: Icon(CupertinoIcons.chart_bar_alt_fill, size: 60), + ), + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Text( + AppLocalizations.of(context).empty_graph_text, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16), + ), + ), + ], + ), + child: SfCartesianChart( + enableAxisAnimation: true, + legend: const Legend( + overflowMode: LegendItemOverflowMode.wrap, + isVisible: true, + position: LegendPosition.bottom), + primaryXAxis: const NumericAxis( + labelStyle: TextStyle(fontWeight: FontWeight.bold), + interval: 1, + decimalPlaces: 0, + ), + primaryYAxis: NumericAxis( + labelStyle: const TextStyle(fontWeight: FontWeight.bold), + labelAlignment: LabelAlignment.center, + labelPosition: ChartDataLabelPosition.inside, + interval: 1, + decimalPlaces: 0, + axisLabelFormatter: (AxisLabelRenderDetails details) { + if (details.value == 0) { + return ChartAxisLabel('', const TextStyle()); + } + return ChartAxisLabel( + '${details.value.toInt()}', const TextStyle()); + }, + ), + series: getCumulativeScores(), + ), + ), + )); + } + + /// Returns a list of LineSeries representing the cumulative scores of each player. + /// Each series contains data points for each round, showing the cumulative score up to that round. + /// The x-axis represents the round number, and the y-axis represents the cumulative score. + List> getCumulativeScores() { + final rounds = widget.gameSession.roundList; + final playerCount = widget.gameSession.players.length; + final playerNames = widget.gameSession.players; + + List> cumulativeScores = List.generate(playerCount, (_) => []); + List runningTotals = List.filled(playerCount, 0); + + for (var round in rounds) { + for (int i = 0; i < playerCount; i++) { + runningTotals[i] += round.scoreUpdates[i]; + cumulativeScores[i].add(runningTotals[i]); + } + } + + const double jitterStep = 0.03; + + /// Create a list of LineSeries for each player + /// Each series contains data points for each round + return List.generate(playerCount, (i) { + final data = List.generate( + cumulativeScores[i].length + 1, + (j) => ( + j, + j == 0 || cumulativeScores[i][j - 1] == 0 + ? 0 // 0 points at the start of the game or when the value is 0 (don't subtract jitter step) + + // Adds a small jitter to the cumulative scores to prevent overlapping data points in the graph. + // The jitter is centered around zero by subtracting playerCount ~/ 2 from the player index i. + : cumulativeScores[i][j - 1] + (i - playerCount ~/ 2) * jitterStep + ), + ); + + /// Create a LineSeries for the player + /// The xValueMapper maps the round number, and the yValueMapper maps the cumulative score. + return LineSeries<(int, num), int>( + name: playerNames[i], + dataSource: data, + xValueMapper: (record, _) => record.$1, + yValueMapper: (record, _) => record.$2, + markerSettings: const MarkerSettings(isVisible: true), + color: lineColors[i], + ); + }); + } +} diff --git a/lib/presentation/views/home/active_game/mode_selection_view.dart b/lib/presentation/views/home/active_game/mode_selection_view.dart new file mode 100644 index 0000000..34777cb --- /dev/null +++ b/lib/presentation/views/home/active_game/mode_selection_view.dart @@ -0,0 +1,85 @@ +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:flutter/cupertino.dart'; + +enum GameMode { + none, + pointLimit, + unlimited, +} + +/// A stateless widget that displays a menu for selecting the game mode. +/// +/// The [ModeSelectionMenu] allows the user to choose between different game modes: +/// - Point limit mode with a specified [pointLimit] +/// - Unlimited mode +/// - Optionally, no default mode if [showDeselection] is true +class ModeSelectionMenu extends StatelessWidget { + final int pointLimit; + final bool showDeselection; + const ModeSelectionMenu( + {super.key, required this.pointLimit, required this.showDeselection}); + + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text(AppLocalizations.of(context).gamemode), + previousPageTitle: + !showDeselection ? AppLocalizations.of(context).new_game : '', + ), + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), + child: CupertinoListTile( + title: Text('$pointLimit ${AppLocalizations.of(context).points}', + style: CustomTheme.modeTitle), + subtitle: Text( + AppLocalizations.of(context) + .point_limit_description(pointLimit), + style: CustomTheme.modeDescription, + maxLines: 3, + ), + onTap: () { + Navigator.pop(context, GameMode.pointLimit); + }, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), + child: CupertinoListTile( + title: Text(AppLocalizations.of(context).unlimited, + style: CustomTheme.modeTitle), + subtitle: Text( + AppLocalizations.of(context).unlimited_description, + style: CustomTheme.modeDescription, + maxLines: 3, + ), + onTap: () { + Navigator.pop(context, GameMode.unlimited); + }, + ), + ), + Visibility( + visible: showDeselection, + child: Padding( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), + child: CupertinoListTile( + title: Text(AppLocalizations.of(context).no_default_mode, + style: CustomTheme.modeTitle), + subtitle: Text( + AppLocalizations.of(context).no_default_description, + style: CustomTheme.modeDescription, + maxLines: 3, + ), + onTap: () { + Navigator.pop(context, GameMode.none); + }, + ), + )), + ], + ), + ); + } +} diff --git a/lib/presentation/views/home/active_game/points_view.dart b/lib/presentation/views/home/active_game/points_view.dart new file mode 100644 index 0000000..eb541e8 --- /dev/null +++ b/lib/presentation/views/home/active_game/points_view.dart @@ -0,0 +1,262 @@ +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/data/game_session.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +/// Displays an overview of points for each player and round in the current game session. +/// +/// The [PointsView] widget shows a table with all rounds and player scores, +/// including score updates and highlights for players who said "Cabo". +/// It uses a Cupertino-style layout and adapts to the number of players. +/// +/// Requires a [GameSession] to provide player and round data. +class PointsView extends StatefulWidget { + final GameSession gameSession; + + const PointsView({super.key, required this.gameSession}); + + @override + State createState() => _PointsViewState(); +} + +class _PointsViewState extends State { + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text(AppLocalizations.of(context).point_overview), + previousPageTitle: AppLocalizations.of(context).overview, + ), + child: SafeArea(child: LayoutBuilder(builder: (context, constraints) { + const double caboFieldWidthFactor = 0.2; + const double tablePadding = 8; + final int playerCount = widget.gameSession.players.length; + const double roundColWidth = 35; + final double playerColWidth = + (constraints.maxWidth - roundColWidth - (tablePadding)) / + playerCount; + + return Column( + children: [ + ConstrainedBox( + constraints: BoxConstraints(maxWidth: constraints.maxWidth), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: tablePadding), + child: DataTable( + dataRowMaxHeight: 0, + dataRowMinHeight: 0, + columnSpacing: 0, + horizontalMargin: 0, + columns: [ + const DataColumn( + label: SizedBox( + width: roundColWidth, + child: Text( + '#', + style: TextStyle(fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + ), + numeric: true, + ), + ...widget.gameSession.players.map( + (player) => DataColumn( + label: SizedBox( + width: playerColWidth, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8), + child: Text( + player, + style: const TextStyle( + fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + softWrap: true, + maxLines: 2, + textAlign: TextAlign.center, + ), + ), + ), + ), + ), + ], + rows: const [], + ), + ), + ), + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: ConstrainedBox( + constraints: + BoxConstraints(maxWidth: constraints.maxWidth), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: tablePadding), + child: DataTable( + dataRowMaxHeight: 75, + dataRowMinHeight: 75, + columnSpacing: 0, + horizontalMargin: 0, + headingRowHeight: 0, + columns: [ + const DataColumn( + label: SizedBox( + width: roundColWidth, + child: Text( + '#', + style: TextStyle(fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + ), + numeric: true, + ), + ...widget.gameSession.players.map( + (player) => DataColumn( + label: SizedBox( + width: playerColWidth, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8), + child: Text( + player, + style: const TextStyle( + fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + softWrap: true, + maxLines: 2, + textAlign: TextAlign.center, + ), + ), + ), + ), + ), + ], + rows: [ + ...List.generate( + widget.gameSession.roundList.length, + (roundIndex) { + final round = + widget.gameSession.roundList[roundIndex]; + return DataRow( + cells: [ + DataCell(Align( + alignment: Alignment.center, + child: Text( + '${roundIndex + 1}', + style: const TextStyle(fontSize: 20), + ), + )), + ...List.generate( + widget.gameSession.players.length, + (playerIndex) { + final int score = + round.scores[playerIndex]; + final int update = + round.scoreUpdates[playerIndex]; + final bool saidCabo = + round.caboPlayerIndex == playerIndex; + return DataCell(Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 6.0), + child: Container( + width: playerColWidth * + (playerCount * + caboFieldWidthFactor), // Adjust width based on amount of players + decoration: BoxDecoration( + color: saidCabo + ? CustomTheme + .buttonBackgroundColor + : CupertinoColors.transparent, + borderRadius: + BorderRadius.circular(5), + ), + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + const SizedBox( + height: 5, + ), + Container( + padding: const EdgeInsets + .symmetric( + horizontal: 6, + vertical: 2), + decoration: BoxDecoration( + color: update <= 0 + ? CustomTheme + .pointLossColor + : CustomTheme + .pointGainColor, + borderRadius: + BorderRadius.circular( + 6), + ), + child: Text( + '${update >= 0 ? '+' : ''}$update', + style: const TextStyle( + color: + CupertinoColors.white, + fontWeight: + FontWeight.bold, + ), + ), + ), + const SizedBox(height: 4), + Text( + '$score', + style: TextStyle( + color: CustomTheme.white, + fontWeight: saidCabo + ? FontWeight.bold + : FontWeight.normal, + ), + ), + ], + ), + ), + ), + )); + }), + ], + ); + }, + ), + DataRow( + cells: [ + const DataCell(Align( + alignment: Alignment.center, + child: Text( + 'Σ', + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold), + ), + )), + ...widget.gameSession.playerScores.map( + (score) => DataCell( + Center( + child: Text( + '$score', + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold), + ), + ), + ), + ), + ], + ), + ], + ), + ), + )), + ), + ], + ); + }))); + } +} diff --git a/lib/presentation/views/home/active_game/round_view.dart b/lib/presentation/views/home/active_game/round_view.dart new file mode 100644 index 0000000..35b85d6 --- /dev/null +++ b/lib/presentation/views/home/active_game/round_view.dart @@ -0,0 +1,557 @@ +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/data/game_session.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/presentation/widgets/custom_button.dart'; +import 'package:cabo_counter/services/local_storage_service.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +/// A view for displaying and managing a single round +/// +/// This widget allows users to input and review scores for each player in a round, +/// select the player who called CABO, and handle special cases such as Kamikaze rounds. +/// It manages the round state, validates input, and coordinates navigation between rounds. +/// +/// Features: +/// - Rotates player order based on the previous round's winner. +/// - Supports Kamikaze rounds with dedicated UI and logic. +/// - Handles score input, validation, and updates to the game session. +/// - Displays bonus point popups when applicable. +/// +/// Requires a [GameSession] and the current [roundNumber]. +class RoundView extends StatefulWidget { + final GameSession gameSession; + final int roundNumber; + const RoundView( + {super.key, required this.roundNumber, required this.gameSession}); + + @override + // ignore: library_private_types_in_public_api + _RoundViewState createState() => _RoundViewState(); +} + +class _RoundViewState extends State { + /// The current game session. + late GameSession gameSession = widget.gameSession; + + /// Index of the player who said CABO. + int _caboPlayerIndex = 0; + + /// Index of the player who has Kamikaze. + /// Default is null (no Kamikaze player). + int? _kamikazePlayerIndex; + + /// List of text controllers for the score text fields. + late final List _scoreControllerList = List.generate( + widget.gameSession.players.length, + (index) => TextEditingController(), + ); + + /// List of focus nodes for the score text fields. + late final List _focusNodeList = List.generate( + widget.gameSession.players.length, + (index) => FocusNode(), + ); + + late List _textFieldKeys; + + @override + void initState() { + print('=== Runde ${widget.roundNumber} geöffnet ==='); + if (widget.roundNumber < widget.gameSession.roundNumber || + widget.gameSession.isGameFinished == true) { + print( + 'Diese wurde bereits gespielt, deshalb werden die alten Punktestaende angezeigt'); + + // If the current round has already been played, the text fields + // are filled with the scores from this round + for (int i = 0; i < _scoreControllerList.length; i++) { + _scoreControllerList[i].text = + gameSession.roundList[widget.roundNumber - 1].scores[i].toString(); + } + _caboPlayerIndex = + gameSession.roundList[widget.roundNumber - 1].caboPlayerIndex; + _kamikazePlayerIndex = + gameSession.roundList[widget.roundNumber - 1].kamikazePlayerIndex; + } + + _textFieldKeys = List.generate( + widget.gameSession.players.length, + (index) => GlobalKey(), + ); + + super.initState(); + } + + @override + @override + Widget build(BuildContext context) { + final bottomInset = MediaQuery.of(context).viewInsets.bottom; + final rotatedPlayers = _getRotatedPlayers(); + final originalIndices = _getOriginalIndices(); + + return CupertinoPageScaffold( + resizeToAvoidBottomInset: false, + navigationBar: CupertinoNavigationBar( + leading: CupertinoButton( + padding: EdgeInsets.zero, + onPressed: () => { + LocalStorageService.saveGameSessions(), + Navigator.pop(context, -1) + }, + child: Text(AppLocalizations.of(context).cancel), + ), + middle: Text(AppLocalizations.of(context).results), + trailing: Visibility( + visible: widget.gameSession.isGameFinished, + child: const Icon( + CupertinoIcons.lock, + size: 25, + ))), + child: Column( + children: [ + Expanded( + child: SingleChildScrollView( + padding: EdgeInsets.only(bottom: 20 + bottomInset), + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 40), + Text( + '${AppLocalizations.of(context).round} ${widget.roundNumber}', + style: CustomTheme.roundTitle), + const SizedBox(height: 10), + Text( + AppLocalizations.of(context).who_said_cabo, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + Padding( + padding: EdgeInsets.symmetric( + horizontal: + widget.gameSession.players.length > 3 ? 5 : 20, + vertical: 10, + ), + child: SizedBox( + height: 60, + child: CupertinoSegmentedControl( + unselectedColor: + CustomTheme.mainElementBackgroundColor, + selectedColor: CustomTheme.primaryColor, + groupValue: _caboPlayerIndex, + children: Map.fromEntries(widget.gameSession.players + .asMap() + .entries + .map((entry) { + final index = entry.key; + final name = entry.value; + return MapEntry( + index, + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 6, + vertical: 8, + ), + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + name, + textAlign: TextAlign.center, + maxLines: 1, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ); + })), + onValueChanged: (value) { + setState(() { + _caboPlayerIndex = value; + }); + }, + ), + ), + ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: rotatedPlayers.length, + itemBuilder: (context, index) { + final originalIndex = originalIndices[index]; + final name = rotatedPlayers[index]; + bool shouldShowMedal = + index == 0 && widget.roundNumber > 1; + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, horizontal: 20), + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: CupertinoListTile( + backgroundColor: CustomTheme.playerTileColor, + title: Row(children: [ + Expanded( + child: Row(children: [ + Text( + name, + overflow: TextOverflow.ellipsis, + ), + Visibility( + visible: shouldShowMedal, + child: const SizedBox(width: 10), + ), + Visibility( + visible: shouldShowMedal, + child: const Icon(FontAwesomeIcons.crown, + size: 15)) + ])) + ]), + subtitle: Text( + '${widget.gameSession.playerScores[originalIndex]}' + ' ${AppLocalizations.of(context).points}'), + trailing: SizedBox( + width: 100, + key: _textFieldKeys[originalIndex], + child: CupertinoTextField( + maxLength: 3, + focusNode: _focusNodeList[originalIndex], + keyboardType: + const TextInputType.numberWithOptions( + signed: true, + decimal: false, + ), + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + textInputAction: index == + widget.gameSession.players.length - 1 + ? TextInputAction.done + : TextInputAction.next, + controller: + _scoreControllerList[originalIndex], + placeholder: + AppLocalizations.of(context).points, + textAlign: TextAlign.center, + onSubmitted: (_) => + _focusNextTextfield(originalIndex), + onChanged: (_) => setState(() {}), + ), + ), + ), + ), + ); + }, + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 0, 0), + child: Center( + heightFactor: 1, + child: CustomButton( + onPressed: () async { + if (await _showKamikazeSheet(context)) { + if (!context.mounted) return; + _endOfRoundNavigation(context, true); + } + }, + child: Text(AppLocalizations.of(context).kamikaze, + style: TextStyle( + color: CustomTheme.kamikazeColor, + )), + ), + ), + ), + ], + ), + ), + ), + ), + KeyboardVisibilityBuilder( + builder: (context, visible) { + if (!visible) { + return Container( + height: 80, + padding: const EdgeInsets.only(bottom: 20), + color: CustomTheme.mainElementBackgroundColor, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + CupertinoButton( + onPressed: _areRoundInputsValid() + ? () { + _endOfRoundNavigation(context, false); + } + : null, + child: Text(AppLocalizations.of(context).done), + ), + if (!widget.gameSession.isGameFinished) + CupertinoButton( + onPressed: _areRoundInputsValid() + ? () { + _endOfRoundNavigation(context, true); + } + : null, + child: Text(AppLocalizations.of(context).next_round), + ), + ], + ), + ); + } else { + return const SizedBox.shrink(); + } + }, + ), + ], + ), + ); + } + + /// Gets the index of the player who won the previous round. + /// Returns 0 in the first round, as there is no previous round. + int _getPreviousRoundWinnerIndex() { + if (widget.roundNumber == 1) { + return 0; // If it's the first round, the order should be the same as the players list. + } + + final List scores = + widget.gameSession.roundList[widget.roundNumber - 2].scoreUpdates; + final int winnerIndex = scores.indexOf(0); + + // Fallback if no player has 0 points, which should not happen in a valid game. + if (winnerIndex == -1) { + return 0; + } + return winnerIndex; + } + + /// Rotates the players list based on the previous round's winner. + List _getRotatedPlayers() { + final winnerIndex = _getPreviousRoundWinnerIndex(); + return [ + widget.gameSession.players[winnerIndex], + ...widget.gameSession.players.sublist(winnerIndex + 1), + ...widget.gameSession.players.sublist(0, winnerIndex) + ]; + } + + /// Gets the original indices of the players by recalculating it from the rotated list. + List _getOriginalIndices() { + final winnerIndex = _getPreviousRoundWinnerIndex(); + return [ + winnerIndex, + ...List.generate(widget.gameSession.players.length - winnerIndex - 1, + (i) => winnerIndex + i + 1), + ...List.generate(winnerIndex, (i) => i) + ]; + } + + /// Shows a Cupertino action sheet to select the player who has Kamikaze. + /// It returns true if a player was selected, false if the action was cancelled. + Future _showKamikazeSheet(BuildContext context) async { + return await showCupertinoModalPopup( + context: context, + builder: (BuildContext context) { + return CupertinoActionSheet( + title: Text(AppLocalizations.of(context).kamikaze), + message: Text(AppLocalizations.of(context).who_has_kamikaze), + actions: widget.gameSession.players.asMap().entries.map((entry) { + final index = entry.key; + final name = entry.value; + return CupertinoActionSheetAction( + onPressed: () { + _kamikazePlayerIndex = index; + Navigator.pop(context, true); + }, + child: Text( + name, + style: TextStyle(color: CustomTheme.kamikazeColor), + ), + ); + }).toList(), + cancelButton: CupertinoActionSheetAction( + onPressed: () => Navigator.pop(context, false), + isDestructiveAction: true, + child: Text(AppLocalizations.of(context).cancel), + ), + ); + }, + ) ?? + false; + } + + /// Focuses the next text field in the list of text fields. + /// [index] is the index of the current text field. + void _focusNextTextfield(int index) { + final originalIndices = _getOriginalIndices(); + final currentPos = originalIndices.indexOf(index); + + if (currentPos < originalIndices.length - 1) { + final nextIndex = originalIndices[currentPos + 1]; + FocusScope.of(context) + .requestFocus(_focusNodeList[originalIndices[currentPos + 1]]); + + final scrollContext = _textFieldKeys[nextIndex].currentContext; + if (scrollContext != null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + Scrollable.ensureVisible( + scrollContext, + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + alignment: 0.55, + ); + }); + } + } else { + _focusNodeList[index].unfocus(); + } + } + + /// Checks if the inputs for the round are valid. + /// Returns true if the inputs are valid, false otherwise. + /// Round Inputs are valid if every player has a score or + /// kamikaze is selected for a player + bool _areRoundInputsValid() { + if (_areTextFieldsEmpty() && _kamikazePlayerIndex == null) return false; + return true; + } + + /// Checks if any of the text fields for the players points are empty. + /// Returns true if any of the text fields is empty, false otherwise. + bool _areTextFieldsEmpty() { + for (TextEditingController t in _scoreControllerList) { + if (t.text.isEmpty) { + return true; + } + } + return false; + } + + /// Finishes the current round. + /// It first determines, ifCalls the [_calculateScoredPoints()] method to calculate the points for + /// every player. If the round is the highest round played in this game, + /// it expands the player score lists. At the end it updates the score + /// array for the game. + List _finishRound() { + print('===================================='); + print('Runde ${widget.roundNumber} beendet'); + // The shown round is smaller than the newest round + if (widget.roundNumber < widget.gameSession.roundNumber) { + print('Da diese Runde bereits gespielt wurde, werden die alten ' + 'Punktestaende ueberschrieben'); + } + if (_kamikazePlayerIndex != null) { + print('${widget.gameSession.players[_kamikazePlayerIndex!]} hat Kamikaze ' + 'und bekommt 0 Punkte'); + print('Alle anderen Spieler bekommen 50 Punkte'); + widget.gameSession + .applyKamikaze(widget.roundNumber, _kamikazePlayerIndex!); + } else { + List roundScores = []; + for (TextEditingController c in _scoreControllerList) { + if (c.text.isNotEmpty) roundScores.add(int.parse(c.text)); + } + widget.gameSession.calculateScoredPoints( + widget.roundNumber, roundScores, _caboPlayerIndex); + } + List bonusPlayers = widget.gameSession.updatePoints(); + if (widget.gameSession.isGameFinished == true) { + print('Das Spiel ist beendet'); + } else if (widget.roundNumber == widget.gameSession.roundNumber) { + widget.gameSession.increaseRound(); + } + return bonusPlayers; + } + + /// Shows a popup dialog with the information which player received the bonus points. + Future _showBonusPopup( + BuildContext context, List bonusPlayers) async { + int pointLimit = widget.gameSession.pointLimit; + int bonusPoints = (pointLimit / 2).round(); + + String resultText = + _getBonusPopupMessageString(pointLimit, bonusPoints, bonusPlayers); + + await showCupertinoDialog( + context: context, + builder: (context) => CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).bonus_points_title), + content: Text(resultText), + actions: [ + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).ok), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + ); + } + + /// Generates the message string for the bonus popup. + /// It takes the [pointLimit], [bonusPoints] and the list of [bonusPlayers] + /// and returns a formatted string. + String _getBonusPopupMessageString( + int pointLimit, int bonusPoints, List bonusPlayers) { + List nameList = + bonusPlayers.map((i) => widget.gameSession.players[i]).toList(); + String resultText = ''; + if (nameList.length == 1) { + resultText = AppLocalizations.of(context).bonus_points_message( + nameList.length, nameList.first, pointLimit, bonusPoints); + } else { + resultText = nameList.length == 2 + ? '${nameList[0]} & ${nameList[1]}' + : '${nameList.sublist(0, nameList.length - 1).join(', ')} & ${nameList.last}'; + resultText = AppLocalizations.of(context).bonus_points_message( + nameList.length, + resultText, + pointLimit, + bonusPoints, + ); + } + return resultText; + } + + /// Handles the navigation for the end of the round. + /// It checks for bonus players and shows a popup, saves the game session, + /// and navigates to the next round or back to the previous screen. + /// It takes the BuildContext [context] and a boolean [navigateToNextRound] to determine + /// if it should navigate to the next round or not. + Future _endOfRoundNavigation( + BuildContext context, bool navigateToNextRound) async { + List bonusPlayersIndices = _finishRound(); + if (bonusPlayersIndices.isNotEmpty) { + await _showBonusPopup(context, bonusPlayersIndices); + } + + LocalStorageService.saveGameSessions(); + + if (context.mounted) { + // If the game is finished, pop the context and return to the previous screen. + if (widget.gameSession.isGameFinished) { + Navigator.pop(context); + return; + } + // If navigateToNextRound is false, pop the context and return to the previous screen. + if (!navigateToNextRound) { + Navigator.pop(context); + return; + } + // If navigateToNextRound is true and the game isn't finished yet, + // pop the context and navigate to the next round. + Navigator.pop(context, widget.roundNumber + 1); + } + } + + @override + void dispose() { + for (final controller in _scoreControllerList) { + controller.dispose(); + } + for (final focusNode in _focusNodeList) { + focusNode.dispose(); + } + super.dispose(); + } +} diff --git a/lib/presentation/views/home/create_game_view.dart b/lib/presentation/views/home/create_game_view.dart new file mode 100644 index 0000000..f181721 --- /dev/null +++ b/lib/presentation/views/home/create_game_view.dart @@ -0,0 +1,504 @@ +import 'package:cabo_counter/core/constants.dart'; +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/data/game_manager.dart'; +import 'package:cabo_counter/data/game_session.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/home/active_game/active_game_view.dart'; +import 'package:cabo_counter/presentation/views/home/active_game/mode_selection_view.dart'; +import 'package:cabo_counter/presentation/widgets/custom_button.dart'; +import 'package:cabo_counter/services/config_service.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; +import 'package:uuid/uuid.dart'; + +enum CreateStatus { + noGameTitle, + noModeSelected, + minPlayers, + maxPlayers, + noPlayerName, +} + +/// A view for creating a new game session in the Cabo Counter app. +/// +/// The [CreateGameView] allows users to input a game title, select a game mode, +/// add and reorder player names, and validate all required fields before +/// starting a new game. It provides feedback dialogs for missing or invalid +/// input and navigates to the active game view upon successful creation. +class CreateGameView extends StatefulWidget { + final GameMode gameMode; + final String? gameTitle; + final List? players; + + const CreateGameView({ + super.key, + this.gameTitle, + this.players, + required this.gameMode, + }); + + @override + // ignore: library_private_types_in_public_api + _CreateGameViewState createState() => _CreateGameViewState(); +} + +class _CreateGameViewState extends State { + final TextEditingController _gameTitleTextController = + TextEditingController(); + + /// List of text controllers for player names. + final List _playerNameTextControllers = [ + TextEditingController() + ]; + + /// List of focus nodes for player name text fields. + final List _playerNameFocusNodes = [FocusNode()]; + + /// Maximum number of players allowed in the game. + final int maxPlayers = 5; + + /// Factor to adjust the view length when the keyboard is visible. + final double keyboardHeightAdjustmentFactor = 0.75; + + /// Variable to hold the selected game mode. + late GameMode gameMode; + + @override + void initState() { + super.initState(); + + gameMode = widget.gameMode; + + _gameTitleTextController.text = widget.gameTitle ?? ''; + + if (widget.players != null) { + _playerNameTextControllers.clear(); + for (var player in widget.players!) { + _playerNameTextControllers.add(TextEditingController(text: player)); + _playerNameFocusNodes.add(FocusNode()); + } + } + } + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: false, + onPopInvokedWithResult: (bool didPop, dynamic result) async { + if (!didPop) { + await _keyboardDelay(); + if (context.mounted) Navigator.pop(context); + } + }, + child: CupertinoPageScaffold( + resizeToAvoidBottomInset: false, + navigationBar: CupertinoNavigationBar( + previousPageTitle: AppLocalizations.of(context).games, + middle: Text(AppLocalizations.of(context).new_game), + ), + child: SafeArea( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).game, + style: CustomTheme.rowTitle, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(15, 10, 10, 0), + child: CupertinoTextField( + decoration: const BoxDecoration(), + maxLength: 20, + prefix: Text(AppLocalizations.of(context).name), + textAlign: TextAlign.right, + placeholder: AppLocalizations.of(context).game_title, + controller: _gameTitleTextController, + onSubmitted: (_) { + _playerNameFocusNodes.isNotEmpty + ? _playerNameFocusNodes[0].requestFocus() + : FocusScope.of(context).unfocus(); + }, + textInputAction: _playerNameFocusNodes.isNotEmpty + ? TextInputAction.next + : TextInputAction.done, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(15, 10, 10, 0), + child: CupertinoTextField( + decoration: const BoxDecoration(), + readOnly: true, + prefix: Text(AppLocalizations.of(context).mode), + suffix: Row( + children: [ + _getDisplayedGameMode(), + const SizedBox(width: 3), + const CupertinoListTileChevron(), + ], + ), + onTap: () async { + await _keyboardDelay(); + + if (context.mounted) { + final selectedMode = await Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ModeSelectionMenu( + pointLimit: ConfigService.getPointLimit(), + showDeselection: false, + ), + ), + ); + + setState(() { + gameMode = selectedMode ?? gameMode; + }); + } + }, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).players, + style: CustomTheme.rowTitle, + ), + ), + ReorderableListView.builder( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.all(8), + itemCount: _playerNameTextControllers.length, + onReorder: (oldIndex, newIndex) { + setState(() { + if (oldIndex < _playerNameTextControllers.length && + newIndex <= _playerNameTextControllers.length) { + if (newIndex > oldIndex) newIndex--; + final item = + _playerNameTextControllers.removeAt(oldIndex); + _playerNameTextControllers.insert(newIndex, item); + } + }); + }, + itemBuilder: (context, index) { + return Padding( + key: ValueKey(index), + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + children: [ + CupertinoButton( + padding: EdgeInsets.zero, + child: Icon( + CupertinoIcons.minus_circle_fill, + color: CustomTheme.red, + size: 25, + ), + onPressed: () { + setState(() { + _playerNameTextControllers[index].dispose(); + _playerNameTextControllers.removeAt(index); + }); + }, + ), + Expanded( + child: CupertinoTextField( + controller: _playerNameTextControllers[index], + focusNode: _playerNameFocusNodes[index], + maxLength: 12, + placeholder: + '${AppLocalizations.of(context).player} ${index + 1}', + padding: const EdgeInsets.all(12), + decoration: const BoxDecoration(), + textInputAction: index + 1 < + _playerNameTextControllers.length + ? TextInputAction.next + : TextInputAction.done, + onSubmitted: (_) { + if (index + 1 < + _playerNameFocusNodes.length) { + _playerNameFocusNodes[index + 1] + .requestFocus(); + } else { + FocusScope.of(context).unfocus(); + } + }, + ), + ), + AnimatedOpacity( + opacity: _playerNameTextControllers.length > 1 + ? 1.0 + : 0.0, + duration: const Duration( + milliseconds: Constants.kFadeInDuration), + child: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: ReorderableDragStartListener( + index: index, + child: const Icon( + CupertinoIcons.line_horizontal_3, + color: CupertinoColors.systemGrey, + ), + ), + ), + ) + ], + ), + ); + }), + Padding( + padding: const EdgeInsets.fromLTRB(8, 0, 8, 50), + child: Stack( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CupertinoButton( + padding: EdgeInsets.zero, + onPressed: null, + child: Icon( + CupertinoIcons.plus_circle_fill, + color: CustomTheme.primaryColor, + size: 25, + ), + ), + ], + ), + Center( + child: CupertinoButton( + padding: const EdgeInsets.symmetric(horizontal: 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Center( + child: Text( + AppLocalizations.of(context).add_player, + style: TextStyle( + color: CustomTheme.primaryColor), + ), + ), + ), + ], + ), + onPressed: () { + if (_playerNameTextControllers.length < + maxPlayers) { + setState(() { + _playerNameTextControllers + .add(TextEditingController()); + _playerNameFocusNodes.add(FocusNode()); + }); + WidgetsBinding.instance + .addPostFrameCallback((_) { + _playerNameFocusNodes.last.requestFocus(); + }); + } else { + _showFeedbackDialog(CreateStatus.maxPlayers); + } + }, + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 50), + child: Center( + child: CustomButton( + child: Text( + AppLocalizations.of(context).create_game, + style: TextStyle( + color: CustomTheme.primaryColor, + ), + ), + onPressed: () async { + await _keyboardDelay(); + _checkAllGameAttributes(); + }, + ), + ), + ), + KeyboardVisibilityBuilder(builder: (context, visible) { + if (visible) { + return SizedBox( + height: MediaQuery.of(context).viewInsets.bottom * + keyboardHeightAdjustmentFactor, + ); + } else { + return const SizedBox.shrink(); + } + }) + ], + ), + )))); + } + + /// Returns a widget that displays the currently selected game mode in the View. + Text _getDisplayedGameMode() { + if (gameMode == GameMode.none) { + return Text(AppLocalizations.of(context).no_mode_selected); + } else if (gameMode == GameMode.pointLimit) { + return Text( + '${ConfigService.getPointLimit()} ${AppLocalizations.of(context).points}', + style: TextStyle(color: CustomTheme.primaryColor)); + } else { + return Text(AppLocalizations.of(context).unlimited, + style: TextStyle(color: CustomTheme.primaryColor)); + } + } + + /// Checks all game attributes before creating a new game. + /// If any attribute is invalid, it shows a feedback dialog. + /// If all attributes are valid, it calls the `_createGame` method. + void _checkAllGameAttributes() { + if (_gameTitleTextController.text == '') { + _showFeedbackDialog(CreateStatus.noGameTitle); + return; + } + + if (gameMode == GameMode.none) { + _showFeedbackDialog(CreateStatus.noModeSelected); + return; + } + + if (_playerNameTextControllers.length < 2) { + _showFeedbackDialog(CreateStatus.minPlayers); + return; + } + + if (!_everyPlayerHasAName()) { + _showFeedbackDialog(CreateStatus.noPlayerName); + return; + } + + _createGame(); + } + + /// Checks if every player has a name. + /// Returns true if all players have a name, false otherwise. + bool _everyPlayerHasAName() { + for (var controller in _playerNameTextControllers) { + if (controller.text == '') { + return false; + } + } + return true; + } + + /// Displays a feedback dialog based on the [CreateStatus]. + void _showFeedbackDialog(CreateStatus status) { + final (title, message) = _getDialogContent(status); + + showCupertinoDialog( + context: context, + builder: (context) { + return CupertinoAlertDialog( + title: Text(title), + content: Text(message), + actions: [ + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).ok), + onPressed: () => Navigator.pop(context), + ), + ], + ); + }); + } + + /// Returns the title and message for the dialog based on the [CreateStatus]. + (String, String) _getDialogContent(CreateStatus status) { + switch (status) { + case CreateStatus.noGameTitle: + return ( + AppLocalizations.of(context).no_gameTitle_title, + AppLocalizations.of(context).no_gameTitle_message + ); + case CreateStatus.noModeSelected: + return ( + AppLocalizations.of(context).no_mode_title, + AppLocalizations.of(context).no_mode_message + ); + + case CreateStatus.minPlayers: + return ( + AppLocalizations.of(context).min_players_title, + AppLocalizations.of(context).min_players_message + ); + case CreateStatus.maxPlayers: + return ( + AppLocalizations.of(context).max_players_title, + AppLocalizations.of(context).max_players_message + ); + case CreateStatus.noPlayerName: + return ( + AppLocalizations.of(context).no_name_title, + AppLocalizations.of(context).no_name_message + ); + } + } + + /// Creates a new gameSession and navigates to the active game view. + /// This method creates a new gameSession object with the provided attributes in the text fields. + /// It then adds the game session to the game manager and navigates to the active game view. + void _createGame() { + var uuid = const Uuid(); + final String id = uuid.v1(); + + List players = []; + for (var controller in _playerNameTextControllers) { + players.add(controller.text); + } + + bool isPointsLimitEnabled = gameMode == GameMode.pointLimit; + + GameSession gameSession = GameSession( + id: id, + createdAt: DateTime.now(), + gameTitle: _gameTitleTextController.text, + players: players, + pointLimit: ConfigService.getPointLimit(), + caboPenalty: ConfigService.getCaboPenalty(), + isPointsLimitEnabled: isPointsLimitEnabled, + ); + gameManager.addGameSession(gameSession); + final session = gameManager.getGameSessionById(id) ?? gameSession; + + Navigator.pushAndRemoveUntil( + context, + CupertinoPageRoute( + builder: (context) => ActiveGameView(gameSession: session)), + (Route route) => route.isFirst, + ); + } + + /// If the keyboard is visible, this method will unfocus the current text field + /// to prevent the keyboard from interfering with the navigation bar. + Future _keyboardDelay() async { + if (!KeyboardVisibilityController().isVisible) { + return; + } else { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: Constants.kKeyboardDelay)); + } + } + + @override + void dispose() { + _gameTitleTextController.dispose(); + for (var controller in _playerNameTextControllers) { + controller.dispose(); + } + for (var focusnode in _playerNameFocusNodes) { + focusnode.dispose(); + } + + super.dispose(); + } +} diff --git a/lib/presentation/views/home/main_menu_view.dart b/lib/presentation/views/home/main_menu_view.dart new file mode 100644 index 0000000..240783a --- /dev/null +++ b/lib/presentation/views/home/main_menu_view.dart @@ -0,0 +1,379 @@ +import 'package:cabo_counter/core/constants.dart'; +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/data/game_manager.dart'; +import 'package:cabo_counter/data/game_session.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/home/active_game/active_game_view.dart'; +import 'package:cabo_counter/presentation/views/home/create_game_view.dart'; +import 'package:cabo_counter/presentation/views/home/settings_view.dart'; +import 'package:cabo_counter/services/config_service.dart'; +import 'package:cabo_counter/services/local_storage_service.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +enum PreRatingDialogDecision { yes, no, cancel } + +enum BadRatingDialogDecision { email, cancel } + +/// Home screen of the app that displays a list of game sessions. +/// +/// The [MainMenuView] is the main entry point for the app's home screen. +/// It displays a list of existing game sessions, allows users to create new games, +/// access settings, and handles user feedback dialogs for app rating and support. +class MainMenuView extends StatefulWidget { + const MainMenuView({super.key}); + + @override + // ignore: library_private_types_in_public_api + _MainMenuViewState createState() => _MainMenuViewState(); +} + +class _MainMenuViewState extends State { + bool _isLoading = true; + + @override + initState() { + super.initState(); + LocalStorageService.loadGameSessions().then((_) { + setState(() { + _isLoading = false; + }); + }); + gameManager.addListener(_updateView); + + WidgetsBinding.instance.addPostFrameCallback((_) async { + precacheImage( + const AssetImage('assets/cabo_counter-logo_rounded.png'), context); + await Constants.rateMyApp.init(); + + if (Constants.rateMyApp.shouldOpenDialog && + Constants.appDevPhase != 'Beta') { + await Future.delayed(const Duration(milliseconds: 600)); + if (!mounted) return; + _handleFeedbackDialog(context); + } + }); + } + + void _updateView() { + if (mounted) setState(() {}); + } + + @override + Widget build(BuildContext context) { + return ListenableBuilder( + listenable: gameManager, + builder: (context, _) { + return CupertinoPageScaffold( + resizeToAvoidBottomInset: false, + navigationBar: CupertinoNavigationBar( + leading: IconButton( + onPressed: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => const SettingsView(), + ), + ).then((_) { + setState(() {}); + }); + }, + icon: const Icon(CupertinoIcons.settings, size: 30)), + middle: Text(AppLocalizations.of(context).games), + trailing: IconButton( + onPressed: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => CreateGameView( + gameMode: ConfigService.getGameMode()), + ), + ), + icon: const Icon(CupertinoIcons.add)), + ), + child: CupertinoPageScaffold( + child: SafeArea( + child: Visibility( + visible: _isLoading, + replacement: Visibility( + visible: gameManager.gameList.isEmpty, + replacement: ListView.separated( + itemCount: gameManager.gameList.length, + separatorBuilder: (context, index) => Divider( + height: 1, + thickness: 0.5, + color: CustomTheme.white.withAlpha(50), + indent: 50, + endIndent: 50, + ), + itemBuilder: (context, index) { + final session = gameManager.gameList[index]; + return ListenableBuilder( + listenable: session, + builder: (context, _) { + return Dismissible( + key: Key(session.id), + background: Container( + color: CustomTheme.red, + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 20.0), + child: const Icon( + CupertinoIcons.delete, + color: CupertinoColors.white, + ), + ), + direction: DismissDirection.endToStart, + confirmDismiss: (direction) async { + return await _showDeleteGamePopup( + context, session.gameTitle); + }, + onDismissed: (direction) { + gameManager.removeGameSessionById(session.id); + }, + dismissThresholds: const { + DismissDirection.startToEnd: 0.6 + }, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 10.0), + child: CupertinoListTile( + backgroundColorActivated: + CustomTheme.backgroundColor, + title: Text(session.gameTitle), + subtitle: Visibility( + visible: session.isGameFinished, + replacement: Text( + '${AppLocalizations.of(context).mode}: ${_translateGameMode(session)}', + style: const TextStyle(fontSize: 14), + ), + child: Text( + '\u{1F947} ${session.winner}', + style: const TextStyle(fontSize: 14), + )), + trailing: Row( + children: [ + const SizedBox( + width: 5, + ), + Text('${session.roundNumber}'), + const SizedBox(width: 3), + const Icon(CupertinoIcons + .arrow_2_circlepath_circle_fill), + const SizedBox(width: 15), + Text('${session.players.length}'), + const SizedBox(width: 3), + const Icon( + CupertinoIcons.person_2_fill), + ], + ), + onTap: () { + final session = + gameManager.gameList[index]; + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ActiveGameView( + gameSession: session), + ), + ).then((_) { + setState(() {}); + }); + }, + ), + ), + ); + }); + }, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 30), + Center( + child: GestureDetector( + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => CreateGameView( + gameMode: ConfigService.getGameMode()), + ), + ), + child: Icon( + CupertinoIcons.plus, + size: 60, + color: CustomTheme.primaryColor, + ), + )), + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 70), + child: Text( + '${AppLocalizations.of(context).empty_text_1}\n${AppLocalizations.of(context).empty_text_2}', + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16), + ), + ), + ], + ), + ), + child: const Center(child: CupertinoActivityIndicator()), + ), + ))); + }); + } + + /// Translates the game mode boolean into the corresponding String. + /// If [pointLimit] is true, it returns '101 Punkte', otherwise it returns 'Unbegrenzt'. + String _translateGameMode(GameSession gameSession) { + if (gameSession.isPointsLimitEnabled) { + return '${gameSession.pointLimit} ${AppLocalizations.of(context).points}'; + } + return AppLocalizations.of(context).unlimited; + } + + /// Handles the feedback dialog when the conditions for rating are met. + /// It shows a dialog asking the user if they like the app, + /// and based on their response, it either opens the rating dialog or an email client for feedback. + Future _handleFeedbackDialog(BuildContext context) async { + final String emailSubject = AppLocalizations.of(context).email_subject; + final String emailBody = AppLocalizations.of(context).email_body; + + final Uri emailUri = Uri( + scheme: 'mailto', + path: Constants.kEmail, + query: 'subject=$emailSubject' + '&body=$emailBody', + ); + + PreRatingDialogDecision preRatingDecision = + await _showPreRatingDialog(context); + BadRatingDialogDecision badRatingDecision = BadRatingDialogDecision.cancel; + + // so that the bad rating dialog is not shown immediately + await Future.delayed(const Duration(milliseconds: Constants.kPopUpDelay)); + + switch (preRatingDecision) { + case PreRatingDialogDecision.yes: + if (context.mounted) Constants.rateMyApp.showStarRateDialog(context); + break; + case PreRatingDialogDecision.no: + if (context.mounted) { + badRatingDecision = await _showBadRatingDialog(context); + } + if (badRatingDecision == BadRatingDialogDecision.email) { + if (context.mounted) { + launchUrl(emailUri); + } + } + break; + case PreRatingDialogDecision.cancel: + break; + } + } + + /// Shows a confirmation dialog to delete all game sessions. + /// Returns true if the user confirms the deletion, false otherwise. + /// [gameTitle] is the title of the game session to be deleted. + Future _showDeleteGamePopup( + BuildContext context, String gameTitle) async { + return await showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text( + AppLocalizations.of(context).delete_game_title, + ), + content: Text(AppLocalizations.of(context) + .delete_game_message(gameTitle)), + actions: [ + CupertinoDialogAction( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(AppLocalizations.of(context).cancel), + ), + CupertinoDialogAction( + isDestructiveAction: true, + isDefaultAction: true, + onPressed: () { + Navigator.of(context).pop(true); + }, + child: Text( + AppLocalizations.of(context).delete, + ), + ) + ]); + }, + ) ?? + false; + } + + /// Shows a dialog asking the user if they like the app. + /// Returns the user's decision as an integer. + /// - PRE_RATING_DIALOG_YES: User likes the app and wants to rate it. + /// - PRE_RATING_DIALOG_NO: User does not like the app and wants to provide feedback. + /// - PRE_RATING_DIALOG_CANCEL: User cancels the dialog. + Future _showPreRatingDialog( + BuildContext context) async { + return await showCupertinoDialog( + context: context, + builder: (BuildContext context) => CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).pre_rating_title), + content: + Text(AppLocalizations.of(context).pre_rating_message), + actions: [ + CupertinoDialogAction( + onPressed: () => Navigator.of(context) + .pop(PreRatingDialogDecision.yes), + isDefaultAction: true, + child: Text(AppLocalizations.of(context).yes), + ), + CupertinoDialogAction( + onPressed: () => + Navigator.of(context).pop(PreRatingDialogDecision.no), + child: Text(AppLocalizations.of(context).no), + ), + CupertinoDialogAction( + onPressed: () => Navigator.of(context).pop(), + isDestructiveAction: true, + child: Text(AppLocalizations.of(context).cancel), + ) + ], + )) ?? + PreRatingDialogDecision.cancel; + } + + /// Shows a dialog asking the user for feedback if they do not like the app. + /// Returns the user's decision as an integer. + /// - BAD_RATING_DIALOG_EMAIL: User wants to send an email with feedback. + /// - BAD_RATING_DIALOG_CANCEL: User cancels the dialog. + Future _showBadRatingDialog( + BuildContext context) async { + return await showCupertinoDialog( + context: context, + builder: (BuildContext context) => CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).bad_rating_title), + content: + Text(AppLocalizations.of(context).bad_rating_message), + actions: [ + CupertinoDialogAction( + isDefaultAction: true, + onPressed: () => Navigator.of(context) + .pop(BadRatingDialogDecision.email), + child: Text(AppLocalizations.of(context).contact_email), + ), + CupertinoDialogAction( + isDestructiveAction: true, + onPressed: () => Navigator.of(context).pop(), + child: Text(AppLocalizations.of(context).cancel)) + ], + )) ?? + BadRatingDialogDecision.cancel; + } + + @override + void dispose() { + gameManager.removeListener(_updateView); + super.dispose(); + } +} diff --git a/lib/presentation/views/home/settings_view.dart b/lib/presentation/views/home/settings_view.dart new file mode 100644 index 0000000..802663c --- /dev/null +++ b/lib/presentation/views/home/settings_view.dart @@ -0,0 +1,302 @@ +import 'package:cabo_counter/core/constants.dart'; +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/home/active_game/mode_selection_view.dart'; +import 'package:cabo_counter/presentation/widgets/custom_form_row.dart'; +import 'package:cabo_counter/presentation/widgets/custom_stepper.dart'; +import 'package:cabo_counter/services/config_service.dart'; +import 'package:cabo_counter/services/local_storage_service.dart'; +import 'package:cabo_counter/services/version_service.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:url_launcher/url_launcher.dart'; + +/// Settings and information page for the app. +/// +/// [SettingsView] is a settings page for the app, allowing users to configure game options, +/// manage game data (import, export, delete), and view app information. +class SettingsView extends StatefulWidget { + const SettingsView({super.key}); + + @override + State createState() => _SettingsViewState(); +} + +class _SettingsViewState extends State { + UniqueKey _stepperKey1 = UniqueKey(); + UniqueKey _stepperKey2 = UniqueKey(); + GameMode defaultMode = ConfigService.getGameMode(); + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text(AppLocalizations.of(context).settings), + previousPageTitle: AppLocalizations.of(context).games, + ), + child: SafeArea( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).points, + style: CustomTheme.rowTitle, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 15, 10, 10), + child: CupertinoFormSection.insetGrouped( + backgroundColor: CustomTheme.backgroundColor, + margin: EdgeInsets.zero, + children: [ + CustomFormRow( + prefixText: AppLocalizations.of(context).cabo_penalty, + prefixIcon: CupertinoIcons.bolt_fill, + suffixWidget: CustomStepper( + key: _stepperKey1, + initialValue: ConfigService.getCaboPenalty(), + minValue: 0, + maxValue: 50, + step: 1, + onChanged: (newCaboPenalty) { + setState(() { + ConfigService.setCaboPenalty(newCaboPenalty); + }); + }, + ), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).point_limit, + prefixIcon: FontAwesomeIcons.bullseye, + suffixWidget: CustomStepper( + key: _stepperKey2, + initialValue: ConfigService.getPointLimit(), + minValue: 30, + maxValue: 1000, + step: 10, + onChanged: (newPointLimit) { + setState(() { + ConfigService.setPointLimit(newPointLimit); + }); + }, + ), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).standard_mode, + prefixIcon: CupertinoIcons.square_stack, + suffixWidget: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + defaultMode == GameMode.none + ? AppLocalizations.of(context).no_default_mode + : (defaultMode == GameMode.pointLimit + ? '${ConfigService.getPointLimit()} ${AppLocalizations.of(context).points}' + : AppLocalizations.of(context).unlimited), + ), + const SizedBox(width: 5), + const CupertinoListTileChevron() + ], + ), + onPressed: () async { + final selectedMode = await Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ModeSelectionMenu( + pointLimit: ConfigService.getPointLimit(), + showDeselection: true, + ), + ), + ); + + setState(() { + defaultMode = selectedMode ?? GameMode.none; + }); + ConfigService.setGameMode(defaultMode); + }, + ), + CustomFormRow( + prefixText: + AppLocalizations.of(context).reset_to_default, + prefixIcon: CupertinoIcons.arrow_counterclockwise, + onPressed: () { + ConfigService.resetConfig(); + setState(() { + _stepperKey1 = UniqueKey(); + _stepperKey2 = UniqueKey(); + defaultMode = ConfigService.getGameMode(); + }); + }, + ) + ])), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).game_data, + style: CustomTheme.rowTitle, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 15, 10, 10), + child: CupertinoFormSection.insetGrouped( + backgroundColor: CustomTheme.backgroundColor, + margin: EdgeInsets.zero, + children: [ + CustomFormRow( + prefixText: AppLocalizations.of(context).import_data, + prefixIcon: CupertinoIcons.square_arrow_down, + onPressed: () async { + final status = + await LocalStorageService.importJsonFile(); + showFeedbackDialog(status); + }, + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).export_data, + prefixIcon: CupertinoIcons.square_arrow_up, + onPressed: () => LocalStorageService.exportGameData(), + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).delete_data, + prefixIcon: CupertinoIcons.trash, + onPressed: () => _deleteAllGames(), + ), + ])), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).app, + style: CustomTheme.rowTitle, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 15, 10, 0), + child: CupertinoFormSection.insetGrouped( + backgroundColor: CustomTheme.backgroundColor, + margin: EdgeInsets.zero, + children: [ + CustomFormRow( + prefixText: AppLocalizations.of(context).wiki, + prefixIcon: CupertinoIcons.book, + onPressed: () => + launchUrl(Uri.parse(Constants.kGithubWikiLink)), + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).error_found, + prefixIcon: FontAwesomeIcons.github, + onPressed: () => + launchUrl(Uri.parse(Constants.kGithubIssuesLink)), + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).app_version, + prefixIcon: CupertinoIcons.tag, + onPressed: null, + suffixWidget: Text(VersionService.getVersion(), + style: TextStyle( + color: CustomTheme.primaryColor, + ))), + CustomFormRow( + prefixText: AppLocalizations.of(context).build, + prefixIcon: CupertinoIcons.number, + onPressed: null, + suffixWidget: Text(VersionService.getBuildNumber(), + style: TextStyle( + color: CustomTheme.primaryColor, + ))), + ])), + const SizedBox(height: 50) + ], + ), + )), + ); + } + + /// Shows a dialog to confirm the deletion of all game data. + /// When confirmed, it deletes all game data from local storage. + void _deleteAllGames() { + showCupertinoDialog( + context: context, + builder: (context) { + return CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).delete_data_title), + content: Text(AppLocalizations.of(context).delete_data_message), + actions: [ + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).cancel), + onPressed: () => Navigator.pop(context), + ), + CupertinoDialogAction( + isDestructiveAction: true, + isDefaultAction: true, + child: Text(AppLocalizations.of(context).delete), + onPressed: () { + LocalStorageService.deleteAllGames(); + Navigator.pop(context); + }, + ), + ], + ); + }, + ); + } + + void showFeedbackDialog(ImportStatus status) { + if (status == ImportStatus.canceled) return; + final (title, message) = _getDialogContent(status); + + showCupertinoDialog( + context: context, + builder: (context) { + return CupertinoAlertDialog( + title: Text(title), + content: Text(message), + actions: [ + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).ok), + onPressed: () => Navigator.pop(context), + ), + ], + ); + }); + } + + (String, String) _getDialogContent(ImportStatus status) { + switch (status) { + case ImportStatus.success: + return ( + AppLocalizations.of(context).import_success_title, + AppLocalizations.of(context).import_success_message + ); + case ImportStatus.validationError: + return ( + AppLocalizations.of(context).import_validation_error_title, + AppLocalizations.of(context).import_validation_error_message + ); + + case ImportStatus.formatError: + return ( + AppLocalizations.of(context).import_format_error_title, + AppLocalizations.of(context).import_format_error_message + ); + case ImportStatus.genericError: + return ( + AppLocalizations.of(context).import_generic_error_title, + AppLocalizations.of(context).import_generic_error_message + ); + case ImportStatus.canceled: + return ('', ''); + } + } +} diff --git a/lib/views/tab_view.dart b/lib/presentation/views/tab_view.dart similarity index 59% rename from lib/views/tab_view.dart rename to lib/presentation/views/tab_view.dart index 4abd411..d24e411 100644 --- a/lib/views/tab_view.dart +++ b/lib/presentation/views/tab_view.dart @@ -1,9 +1,18 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/views/information_view.dart'; -import 'package:cabo_counter/views/main_menu_view.dart'; +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/about/about_view.dart'; +import 'package:cabo_counter/presentation/views/home/main_menu_view.dart'; import 'package:flutter/cupertino.dart'; +/// TabBar for navigating between the main menu and about section. +/// +/// [TabView] is a [StatefulWidget] that provides a tabbed interface for navigating +/// between the main menu and the about section of the app. It uses a +/// [CupertinoTabScaffold] with two tabs: +/// - Home (MainMenuView) +/// - About (AboutView) +/// +/// The tab labels are provided via localization. class TabView extends StatefulWidget { const TabView({super.key}); @@ -16,8 +25,9 @@ class _TabViewState extends State { @override Widget build(BuildContext context) { return CupertinoTabScaffold( + resizeToAvoidBottomInset: false, tabBar: CupertinoTabBar( - backgroundColor: CustomTheme.backgroundTintColor, + backgroundColor: CustomTheme.mainElementBackgroundColor, iconSize: 27, height: 55, items: [ @@ -39,7 +49,7 @@ class _TabViewState extends State { if (index == 0) { return const MainMenuView(); } else { - return const InformationView(); + return const AboutView(); } }); }, diff --git a/lib/presentation/widgets/custom_button.dart b/lib/presentation/widgets/custom_button.dart new file mode 100644 index 0000000..2b68b44 --- /dev/null +++ b/lib/presentation/widgets/custom_button.dart @@ -0,0 +1,23 @@ +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:flutter/cupertino.dart'; + +/// A customizable button widget using Cupertino style. +/// +/// Displays a button with a child widget and optional callback. +/// The button uses a medium size, rounded corners, and a custom background color. +class CustomButton extends StatelessWidget { + final Widget child; + final VoidCallback? onPressed; + const CustomButton({super.key, required this.child, this.onPressed}); + + @override + Widget build(BuildContext context) { + return CupertinoButton( + sizeStyle: CupertinoButtonSize.medium, + borderRadius: BorderRadius.circular(12), + color: CustomTheme.buttonBackgroundColor, + onPressed: onPressed, + child: child, + ); + } +} diff --git a/lib/presentation/widgets/custom_form_row.dart b/lib/presentation/widgets/custom_form_row.dart new file mode 100644 index 0000000..751e487 --- /dev/null +++ b/lib/presentation/widgets/custom_form_row.dart @@ -0,0 +1,58 @@ +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/presentation/widgets/custom_stepper.dart'; +import 'package:flutter/cupertino.dart'; + +/// A customizable form row widget with a prefix icon, text, and optional suffix widget. +/// +/// Displays a row with an icon and text on the left side. +/// Optionally, a suffix widget (e.g. a stepper) can be shown on the right side. +/// The row is styled as a [CupertinoButton] and can react to taps. +class CustomFormRow extends StatefulWidget { + final String prefixText; + final IconData prefixIcon; + final Widget? suffixWidget; + final void Function()? onPressed; + const CustomFormRow({ + super.key, + required this.prefixText, + required this.prefixIcon, + this.onPressed, + this.suffixWidget, + }); + + @override + State createState() => _CustomFormRowState(); +} + +class _CustomFormRowState extends State { + late Widget suffixWidget; + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + suffixWidget = widget.suffixWidget ?? const SizedBox.shrink(); + return CupertinoButton( + padding: EdgeInsets.zero, + onPressed: widget.onPressed, + child: CupertinoFormRow( + prefix: Row( + children: [ + Icon( + widget.prefixIcon, + color: CustomTheme.primaryColor, + ), + const SizedBox(width: 10), + Text(widget.prefixText), + ], + ), + padding: suffixWidget is CustomStepper + ? const EdgeInsets.fromLTRB(15, 0, 0, 0) + : const EdgeInsets.symmetric(vertical: 10, horizontal: 15), + child: suffixWidget, + ), + ); + } +} diff --git a/lib/widgets/stepper.dart b/lib/presentation/widgets/custom_stepper.dart similarity index 60% rename from lib/widgets/stepper.dart rename to lib/presentation/widgets/custom_stepper.dart index 879235e..be41048 100644 --- a/lib/widgets/stepper.dart +++ b/lib/presentation/widgets/custom_stepper.dart @@ -1,12 +1,24 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:flutter/cupertino.dart'; // Für iOS-Style -class Stepper extends StatefulWidget { +/// A custom stepper widget for incrementing and decrementing a value. +/// +/// The [CustomStepper] widget allows increasing and decreasing a value +/// within a defined range ([minValue] to [maxValue]) in fixed steps. +/// +/// Properties: +/// - [minValue]: The minimum value. +/// - [maxValue]: The maximum value. +/// - [initialValue]: The initial value (optional, defaults to [minValue]). +/// - [step]: The step size. +/// - [onChanged]: Callback triggered when the value changes. +class CustomStepper extends StatefulWidget { final int minValue; final int maxValue; final int? initialValue; final int step; final ValueChanged onChanged; - const Stepper({ + const CustomStepper({ super.key, required this.minValue, required this.maxValue, @@ -17,10 +29,10 @@ class Stepper extends StatefulWidget { @override // ignore: library_private_types_in_public_api - _StepperState createState() => _StepperState(); + _CustomStepperState createState() => _CustomStepperState(); } -class _StepperState extends State { +class _CustomStepperState extends State { late int _value; @override @@ -34,18 +46,20 @@ class _StepperState extends State { Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, children: [ CupertinoButton( - padding: const EdgeInsets.all(8), + padding: EdgeInsets.zero, onPressed: _decrement, child: const Icon(CupertinoIcons.minus), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Text('$_value', style: const TextStyle(fontSize: 18)), + child: Text('$_value', + style: TextStyle(fontSize: 18, color: CustomTheme.white)), ), CupertinoButton( - padding: const EdgeInsets.all(8), + padding: EdgeInsets.zero, onPressed: _increment, child: const Icon(CupertinoIcons.add), ), diff --git a/lib/services/config_service.dart b/lib/services/config_service.dart index 1c8275a..419e461 100644 --- a/lib/services/config_service.dart +++ b/lib/services/config_service.dart @@ -1,55 +1,109 @@ -import 'package:cabo_counter/utility/globals.dart'; +import 'package:cabo_counter/presentation/views/home/active_game/mode_selection_view.dart'; import 'package:shared_preferences/shared_preferences.dart'; -/// This class handles the configuration settings for the app. -/// It uses SharedPreferences to store and retrieve the personal configuration of the app. -/// Currently it provides methods to initialize, get, and set the point limit and cabo penalty. +/// A service class for managing and persisting app configuration settings using `SharedPreferences`. +/// +/// Provides methods to initialize, retrieve, update, and reset configuration values such as point limit, +/// cabo penalty, and game mode. Ensures that user preferences are stored locally and persist across app restarts. class ConfigService { + // Keys for the stored values static const String _keyPointLimit = 'pointLimit'; static const String _keyCaboPenalty = 'caboPenalty'; - static const int _defaultPointLimit = 100; // Default Value - static const int _defaultCaboPenalty = 5; // Default Value + static const String _keyGameMode = 'gameMode'; + // Actual values used in the app + static int _pointLimit = 100; + static int _caboPenalty = 5; + static int _gameMode = -1; + // Default values + static const int _defaultPointLimit = 100; + static const int _defaultCaboPenalty = 5; + static const int _defaultGameMode = -1; static Future initConfig() async { final prefs = await SharedPreferences.getInstance(); - // Default values only set if they are not already set - prefs.setInt( - _keyPointLimit, prefs.getInt(_keyPointLimit) ?? _defaultPointLimit); - prefs.setInt( - _keyCaboPenalty, prefs.getInt(_keyCaboPenalty) ?? _defaultCaboPenalty); + // Initialize pointLimit, caboPenalty, and gameMode from SharedPreferences + // If they are not set, use the default values + _pointLimit = prefs.getInt(_keyPointLimit) ?? _defaultPointLimit; + _caboPenalty = prefs.getInt(_keyCaboPenalty) ?? _defaultCaboPenalty; + _gameMode = prefs.getInt(_keyGameMode) ?? _defaultGameMode; + + // Save the initial values to SharedPreferences + prefs.setInt(_keyPointLimit, _pointLimit); + prefs.setInt(_keyCaboPenalty, _caboPenalty); + prefs.setInt(_keyGameMode, _gameMode); } - /// Getter for the point limit. - static Future getPointLimit() async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getInt(_keyPointLimit) ?? _defaultPointLimit; + /// Retrieves the current game mode. + /// + /// The game mode is determined based on the stored integer value: + /// - `0`: [GameMode.pointLimit] + /// - `1`: [GameMode.unlimited] + /// - Any other value: [GameMode.none] (-1 is used as a default for no mode) + /// + /// Returns the corresponding [GameMode] enum value. + static GameMode getGameMode() { + switch (_gameMode) { + case 0: + return GameMode.pointLimit; + case 1: + return GameMode.unlimited; + default: + return GameMode.none; + } } + /// Sets the game mode for the application. + /// + /// [newGameMode] is the new game mode to be set. It can be one of the following: + /// - `GameMode.pointLimit`: The game ends when a pleayer reaches the point limit. + /// - `GameMode.unlimited`: Every game goes for infinity until you end it. + /// - `GameMode.none`: No default mode set. + /// + /// This method updates the `_gameMode` field and persists the value in `SharedPreferences`. + static Future setGameMode(GameMode newGameMode) async { + int gameMode; + switch (newGameMode) { + case GameMode.pointLimit: + gameMode = 0; + break; + case GameMode.unlimited: + gameMode = 1; + break; + default: + gameMode = -1; + } + + final prefs = await SharedPreferences.getInstance(); + await prefs.setInt(_keyGameMode, gameMode); + _gameMode = gameMode; + } + + static int getPointLimit() => _pointLimit; + /// Setter for the point limit. /// [newPointLimit] is the new point limit to be set. static Future setPointLimit(int newPointLimit) async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_keyPointLimit, newPointLimit); + _pointLimit = newPointLimit; } - /// Getter for the cabo penalty. - static Future getCaboPenalty() async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getInt(_keyCaboPenalty) ?? _defaultCaboPenalty; - } + static int getCaboPenalty() => _caboPenalty; /// Setter for the cabo penalty. /// [newCaboPenalty] is the new cabo penalty to be set. static Future setCaboPenalty(int newCaboPenalty) async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_keyCaboPenalty, newCaboPenalty); + _caboPenalty = newCaboPenalty; } /// Resets the configuration to default values. static Future resetConfig() async { - Globals.pointLimit = _defaultPointLimit; - Globals.caboPenalty = _defaultCaboPenalty; + ConfigService._pointLimit = _defaultPointLimit; + ConfigService._caboPenalty = _defaultCaboPenalty; + ConfigService._gameMode = _defaultGameMode; final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_keyPointLimit, _defaultPointLimit); await prefs.setInt(_keyCaboPenalty, _defaultCaboPenalty); diff --git a/lib/services/local_storage_service.dart b/lib/services/local_storage_service.dart index 2004845..520f5ca 100644 --- a/lib/services/local_storage_service.dart +++ b/lib/services/local_storage_service.dart @@ -9,11 +9,19 @@ import 'package:flutter/services.dart'; import 'package:json_schema/json_schema.dart'; import 'package:path_provider/path_provider.dart'; +enum ImportStatus { + success, + canceled, + validationError, + formatError, + genericError +} + class LocalStorageService { static const String _fileName = 'game_data.json'; - /// Writes the game session list to a JSON file and returns it as string. - static String getJsonFile() { + /// Writes the game session list to a JSON file and returns it as string. + static String _getGameDataAsJsonFile() { final jsonFile = gameManager.gameList.map((session) => session.toJson()).toList(); return json.encode(jsonFile); @@ -31,7 +39,7 @@ class LocalStorageService { print('[local_storage_service.dart] Versuche, Daten zu speichern...'); try { final file = await _getFilePath(); - final jsonFile = getJsonFile(); + final jsonFile = _getGameDataAsJsonFile(); await file.writeAsString(jsonFile); print( '[local_storage_service.dart] Die Spieldaten wurden zwischengespeichert.'); @@ -47,6 +55,7 @@ class LocalStorageService { try { final file = await _getFilePath(); + // Check if the file exists if (!await file.exists()) { print( '[local_storage_service.dart] Es existiert noch keine Datei mit Spieldaten'); @@ -57,12 +66,14 @@ class LocalStorageService { '[local_storage_service.dart] Es existiert bereits eine Datei mit Spieldaten'); final jsonString = await file.readAsString(); + // Check if the file is empty if (jsonString.isEmpty) { print('[local_storage_service.dart] Die gefundene Datei ist leer'); return false; } - if (!await validateJsonSchema(jsonString)) { + // Validate the JSON schema + if (!await _validateJsonSchema(jsonString, true)) { print( '[local_storage_service.dart] Die Datei konnte nicht validiert werden'); gameManager.gameList = []; @@ -78,6 +89,11 @@ class LocalStorageService { GameSession.fromJson(jsonItem as Map)) .toList(); + for (GameSession session in gameManager.gameList) { + print( + '[local_storage_service.dart] Geladene Session: ${session.gameTitle} - ${session.id}'); + } + print( '[local_storage_service.dart] Die Spieldaten wurden erfolgreich geladen und verarbeitet'); return true; @@ -89,19 +105,27 @@ class LocalStorageService { } } - /// Opens the file picker to save a JSON file with the current game data. - static Future exportJsonFile() async { - final jsonString = getJsonFile(); + /// Opens the file picker to export game data as a JSON file. + /// This method will export the given [jsonString] as a JSON file. It opens + /// the file picker with the choosen [fileName]. + static Future _exportJsonData( + String jsonString, + String fileName, + ) async { try { final bytes = Uint8List.fromList(utf8.encode(jsonString)); - final result = await FileSaver.instance.saveAs( - name: 'cabo_counter_data', + final path = await FileSaver.instance.saveAs( + name: fileName, bytes: bytes, ext: 'json', mimeType: MimeType.json, ); - print( - '[local_storage_service.dart] Die Spieldaten wurden exportiert. Dateipfad: $result'); + if (path == null) { + print('[local_storage_service.dart]: Export abgebrochen'); + } else { + print( + '[local_storage_service.dart] Die Spieldaten wurden exportiert. Dateipfad: $path'); + } return true; } catch (e) { print( @@ -110,45 +134,82 @@ class LocalStorageService { } } + /// Opens the file picker to export all game sessions as a JSON file. + static Future exportGameData() async { + String jsonString = _getGameDataAsJsonFile(); + String fileName = 'cabo_counter-game_data'; + return _exportJsonData(jsonString, fileName); + } + + /// Opens the file picker to save a single game session as a JSON file. + static Future exportSingleGameSession(GameSession session) async { + String jsonString = json.encode(session.toJson()); + String fileName = 'cabo_counter-game_${session.id.substring(0, 7)}'; + return _exportJsonData(jsonString, fileName); + } + /// Opens the file picker to import a JSON file and loads the game data from it. - static Future importJsonFile() async { - final result = await FilePicker.platform.pickFiles( - dialogTitle: 'Wähle eine Datei mit Spieldaten aus', + static Future importJsonFile() async { + final path = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['json'], ); - if (result == null) { + if (path == null) { print( '[local_storage_service.dart] Der Filepicker-Dialog wurde abgebrochen'); - return false; + return ImportStatus.canceled; } try { - final jsonString = await _readFileContent(result.files.single); + final jsonString = await _readFileContent(path.files.single); - if (!await validateJsonSchema(jsonString)) { - return false; + // Checks if the JSON String is in the gameList format + if (await _validateJsonSchema(jsonString, true)) { + final jsonData = json.decode(jsonString) as List; + List importedList = jsonData + .map((jsonItem) => + GameSession.fromJson(jsonItem as Map)) + .toList(); + + for (GameSession s in importedList) { + _importSession(s); + } + } else if (await _validateJsonSchema(jsonString, false)) { + // Checks if the JSON String is in the single game format + final jsonData = json.decode(jsonString) as Map; + _importSession(GameSession.fromJson(jsonData)); + } else { + return ImportStatus.validationError; } - final jsonData = json.decode(jsonString) as List; - gameManager.gameList = jsonData - .map((jsonItem) => - GameSession.fromJson(jsonItem as Map)) - .toList(); + print( - '[local_storage_service.dart] Die Datei wurde erfolgreich Importiertn'); - return true; + '[local_storage_service.dart] Die Datei wurde erfolgreich Importiert'); + await saveGameSessions(); + return ImportStatus.success; } on FormatException catch (e) { print( '[local_storage_service.dart] Ungültiges JSON-Format. Exception: $e'); - return false; + return ImportStatus.formatError; } on Exception catch (e) { print( '[local_storage_service.dart] Fehler beim Dateizugriff. Exception: $e'); - return false; + return ImportStatus.genericError; } } + /// Imports a single game session into the gameList. + static Future _importSession(GameSession session) async { + if (gameManager.gameExistsInGameList(session.id)) { + print( + '[local_storage_service.dart] Die Session mit der ID ${session.id} existiert bereits. Sie wird überschrieben.'); + gameManager.removeGameSessionById(session.id); + } + gameManager.addGameSession(session); + print( + '[local_storage_service.dart] Die Session mit der ID ${session.id} wurde erfolgreich importiert.'); + } + /// Helper method to read file content from either bytes or path static Future _readFileContent(PlatformFile file) async { if (file.bytes != null) return utf8.decode(file.bytes!); @@ -158,15 +219,28 @@ class LocalStorageService { } /// Validates the JSON data against the schema. - static Future validateJsonSchema(String jsonString) async { + /// This method checks if the provided [jsonString] is valid against the + /// JSON schema. It takes a boolean [isGameList] to determine + /// which schema to use (game list or single game). + static Future _validateJsonSchema( + String jsonString, bool isGameList) async { + final String schemaString; + + if (isGameList) { + schemaString = + await rootBundle.loadString('assets/game_list-schema.json'); + } else { + schemaString = await rootBundle.loadString('assets/game-schema.json'); + } + try { - final schemaString = await rootBundle.loadString('assets/schema.json'); final schema = JsonSchema.create(json.decode(schemaString)); final jsonData = json.decode(jsonString); final result = schema.validate(jsonData); if (result.isValid) { - print('[local_storage_service.dart] JSON ist erfolgreich validiert.'); + print( + '[local_storage_service.dart] JSON ist erfolgreich validiert. Typ: ${isGameList ? 'Game List' : 'Single Game'}'); return true; } print( diff --git a/lib/services/version_service.dart b/lib/services/version_service.dart new file mode 100644 index 0000000..6511c69 --- /dev/null +++ b/lib/services/version_service.dart @@ -0,0 +1,32 @@ +import 'package:cabo_counter/core/constants.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +class VersionService { + static String _version = '-.-.-'; + static String _buildNumber = '-'; + + static Future init() async { + var packageInfo = await PackageInfo.fromPlatform(); + _version = packageInfo.version; + _buildNumber = packageInfo.buildNumber; + } + + static String getVersionNumber() { + return _version; + } + + static String getVersion() { + if (_version == '-.-.-') { + return getVersionNumber(); + } + return '${Constants.appDevPhase} $_version'; + } + + static String getBuildNumber() { + return _buildNumber; + } + + static String getVersionWithBuild() { + return '$_version ($_buildNumber)'; + } +} diff --git a/lib/utility/custom_theme.dart b/lib/utility/custom_theme.dart deleted file mode 100644 index 77a2f5b..0000000 --- a/lib/utility/custom_theme.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -class CustomTheme { - static Color white = CupertinoColors.white; - static Color primaryColor = CupertinoColors.systemGreen; - static Color backgroundColor = const Color(0xFF101010); - static Color backgroundTintColor = CupertinoColors.darkBackgroundGray; - - static TextStyle modeTitle = TextStyle( - color: primaryColor, - fontSize: 20, - fontWeight: FontWeight.bold, - ); - - static const TextStyle modeDescription = TextStyle( - fontSize: 16, - ); - - static TextStyle rowTitle = TextStyle( - fontSize: 20, - color: primaryColor, - fontWeight: FontWeight.bold, - ); - - static TextStyle roundTitle = TextStyle( - fontSize: 60, - color: white, - fontWeight: FontWeight.bold, - ); -} diff --git a/lib/utility/globals.dart b/lib/utility/globals.dart deleted file mode 100644 index 54cf2d0..0000000 --- a/lib/utility/globals.dart +++ /dev/null @@ -1,5 +0,0 @@ -class Globals { - static int pointLimit = 100; - static int caboPenalty = 5; - static String appDevPhase = 'Beta'; -} diff --git a/lib/views/active_game_view.dart b/lib/views/active_game_view.dart deleted file mode 100644 index c0d359f..0000000 --- a/lib/views/active_game_view.dart +++ /dev/null @@ -1,207 +0,0 @@ -import 'package:cabo_counter/data/models/game_session.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/views/graph_view.dart'; -import 'package:cabo_counter/views/round_view.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class ActiveGameView extends StatefulWidget { - final GameSession gameSession; - const ActiveGameView({super.key, required this.gameSession}); - - @override - // ignore: library_private_types_in_public_api - _ActiveGameViewState createState() => _ActiveGameViewState(); -} - -class _ActiveGameViewState extends State { - @override - Widget build(BuildContext context) { - return ListenableBuilder( - listenable: widget.gameSession, - builder: (context, _) { - List sortedPlayerIndices = _getSortedPlayerIndices(); - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text(widget.gameSession.gameTitle), - ), - child: SafeArea( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).players, - style: CustomTheme.rowTitle, - ), - ), - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: widget.gameSession.players.length, - itemBuilder: (BuildContext context, int index) { - int playerIndex = sortedPlayerIndices[index]; - return CupertinoListTile( - title: Row( - children: [ - _getPlacementPrefix(index), - const SizedBox(width: 5), - Text( - widget.gameSession.players[playerIndex], - style: const TextStyle( - fontWeight: FontWeight.bold), - ), - ], - ), - trailing: Row( - children: [ - const SizedBox(width: 5), - Text( - '${widget.gameSession.playerScores[playerIndex]} ' - '${AppLocalizations.of(context).points}') - ], - ), - ); - }, - ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).rounds, - style: CustomTheme.rowTitle, - ), - ), - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: widget.gameSession.roundNumber, - itemBuilder: (BuildContext context, int index) { - return Padding( - padding: const EdgeInsets.all(1), - child: CupertinoListTile( - backgroundColorActivated: - CustomTheme.backgroundColor, - title: Text( - '${AppLocalizations.of(context).round} ${index + 1}', - ), - trailing: index + 1 != - widget.gameSession.roundNumber || - widget.gameSession.isGameFinished == - true - ? (const Text('\u{2705}', - style: TextStyle(fontSize: 22))) - : const Text('\u{23F3}', - style: TextStyle(fontSize: 22)), - onTap: () async { - // ignore: unused_local_variable - final val = await Navigator.of(context, - rootNavigator: true) - .push( - CupertinoPageRoute( - fullscreenDialog: true, - builder: (context) => RoundView( - gameSession: widget.gameSession, - roundNumber: index + 1), - ), - ); - }, - )); - }, - ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).game, - style: CustomTheme.rowTitle, - ), - ), - Column( - children: [ - CupertinoListTile( - backgroundColorActivated: - CustomTheme.backgroundColor, - title: Text( - AppLocalizations.of(context).statistics, - ), - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => GraphView( - gameSession: widget.gameSession, - )))), - CupertinoListTile( - title: - Text(AppLocalizations.of(context).delete_game, - style: const TextStyle( - color: Colors.white30, - )), - onTap: () {}, - ), - CupertinoListTile( - title: Text( - AppLocalizations.of(context) - .new_game_same_settings, - style: const TextStyle( - color: Colors.white30, - ))), - CupertinoListTile( - title: - Text(AppLocalizations.of(context).export_game, - style: const TextStyle( - color: Colors.white30, - )), - ), - ], - ) - ], - ), - ), - )); - }); - } - - /// Returns a list of player indices sorted by their scores in - /// ascending order. - List _getSortedPlayerIndices() { - List playerIndices = - List.generate(widget.gameSession.players.length, (index) => index); - // Sort the indices based on the summed points - playerIndices.sort((a, b) { - int scoreA = widget.gameSession.playerScores[a]; - int scoreB = widget.gameSession.playerScores[b]; - return scoreA.compareTo(scoreB); - }); - return playerIndices; - } - - /// Returns a widget that displays the placement prefix based on the index. - /// First three places are represented by medals, and the rest are numbered. - /// [index] is the index of the player in the descending sorted list. - Widget _getPlacementPrefix(int index) { - switch (index) { - case 0: - return const Text( - '\u{1F947}', - style: TextStyle(fontSize: 22), - ); - case 1: - return const Text( - '\u{1F948}', - style: TextStyle(fontSize: 22), - ); - case 2: - return const Text( - '\u{1F949}', - style: TextStyle(fontSize: 22), - ); - default: - return Text( - ' ${index + 1}.', - style: const TextStyle(fontWeight: FontWeight.bold), - ); - } - } -} diff --git a/lib/views/create_game_view.dart b/lib/views/create_game_view.dart deleted file mode 100644 index 4e6720b..0000000 --- a/lib/views/create_game_view.dart +++ /dev/null @@ -1,328 +0,0 @@ -import 'package:cabo_counter/data/models/game_manager.dart'; -import 'package:cabo_counter/data/models/game_session.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/utility/globals.dart'; -import 'package:cabo_counter/views/active_game_view.dart'; -import 'package:cabo_counter/views/mode_selection_view.dart'; -import 'package:flutter/cupertino.dart'; - -class CreateGame extends StatefulWidget { - const CreateGame({super.key}); - - @override - // ignore: library_private_types_in_public_api - _CreateGameState createState() => _CreateGameState(); -} - -class _CreateGameState extends State { - final List _playerNameTextControllers = [ - TextEditingController() - ]; - final TextEditingController _gameTitleTextController = - TextEditingController(); - - /// Maximum number of players allowed in the game. - final int maxPlayers = 5; - - /// Variable to store the selected game mode. - bool? selectedMode; - - @override - Widget build(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - previousPageTitle: AppLocalizations.of(context).overview, - middle: Text(AppLocalizations.of(context).new_game), - ), - child: SafeArea( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).game, - style: CustomTheme.rowTitle, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(15, 10, 10, 0), - child: CupertinoTextField( - decoration: const BoxDecoration(), - maxLength: 16, - prefix: Text(AppLocalizations.of(context).name), - textAlign: TextAlign.right, - placeholder: AppLocalizations.of(context).game_title, - controller: _gameTitleTextController, - ), - ), - // Spielmodus-Auswahl mit Chevron - Padding( - padding: const EdgeInsets.fromLTRB(15, 10, 10, 0), - child: CupertinoTextField( - decoration: const BoxDecoration(), - readOnly: true, - prefix: Text(AppLocalizations.of(context).mode), - suffix: Row( - children: [ - Text( - selectedMode == null - ? AppLocalizations.of(context).select_mode - : (selectedMode! - ? '${Globals.pointLimit} ${AppLocalizations.of(context).points}' - : AppLocalizations.of(context).unlimited), - ), - const SizedBox(width: 3), - const CupertinoListTileChevron(), - ], - ), - onTap: () async { - final selected = await Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => ModeSelectionMenu( - pointLimit: Globals.pointLimit, - ), - ), - ); - - if (selected != null) { - setState(() { - selectedMode = selected; - }); - } - }, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).players, - style: CustomTheme.rowTitle, - ), - ), - Expanded( - child: ListView.builder( - itemCount: _playerNameTextControllers.length + - 1, // +1 für den + Button - itemBuilder: (context, index) { - if (index == _playerNameTextControllers.length) { - // + Button als letztes Element - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: CupertinoButton( - padding: EdgeInsets.zero, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon( - CupertinoIcons.add_circled, - color: CupertinoColors.activeGreen, - size: 25, - ), - const SizedBox(width: 8), - Text( - AppLocalizations.of(context).add_player, - style: const TextStyle( - color: CupertinoColors.activeGreen, - ), - ), - ], - ), - onPressed: () { - if (_playerNameTextControllers.length < maxPlayers) { - setState(() { - _playerNameTextControllers - .add(TextEditingController()); - }); - } else { - showCupertinoDialog( - context: context, - builder: (context) => CupertinoAlertDialog( - title: Text(AppLocalizations.of(context) - .max_players_title), - content: Text(AppLocalizations.of(context) - .max_players_message), - actions: [ - CupertinoDialogAction( - child: - Text(AppLocalizations.of(context).ok), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ); - } - }, - ), - ); - } else { - // Spieler-Einträge - return Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, horizontal: 5), - child: Row( - children: [ - CupertinoButton( - padding: EdgeInsets.zero, - child: const Icon( - CupertinoIcons.minus_circle_fill, - color: CupertinoColors.destructiveRed, - size: 25, - ), - onPressed: () { - setState(() { - _playerNameTextControllers[index].dispose(); - _playerNameTextControllers.removeAt(index); - }); - }, - ), - Expanded( - child: CupertinoTextField( - controller: _playerNameTextControllers[index], - placeholder: - '${AppLocalizations.of(context).player} ${index + 1}', - padding: const EdgeInsets.all(12), - decoration: const BoxDecoration(), - ), - ), - ], - ), - ); - } - }, - ), - ), - Center( - child: CupertinoButton( - padding: EdgeInsets.zero, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - AppLocalizations.of(context).create_game, - style: const TextStyle( - color: CupertinoColors.activeGreen, - ), - ), - ], - ), - onPressed: () async { - if (_gameTitleTextController.text == '') { - showCupertinoDialog( - context: context, - builder: (context) => CupertinoAlertDialog( - title: Text( - AppLocalizations.of(context).no_gameTitle_title), - content: Text( - AppLocalizations.of(context).no_gameTitle_message), - actions: [ - CupertinoDialogAction( - child: Text(AppLocalizations.of(context).ok), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ); - return; - } - if (selectedMode == null) { - showCupertinoDialog( - context: context, - builder: (context) => CupertinoAlertDialog( - title: Text(AppLocalizations.of(context).no_mode_title), - content: - Text(AppLocalizations.of(context).no_mode_message), - actions: [ - CupertinoDialogAction( - child: Text(AppLocalizations.of(context).ok), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ); - return; - } - if (_playerNameTextControllers.length < 2) { - showCupertinoDialog( - context: context, - builder: (context) => CupertinoAlertDialog( - title: Text( - AppLocalizations.of(context).min_players_title), - content: Text( - AppLocalizations.of(context).min_players_message), - actions: [ - CupertinoDialogAction( - child: Text(AppLocalizations.of(context).ok), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ); - return; - } - if (!everyPlayerHasAName()) { - showCupertinoDialog( - context: context, - builder: (context) => CupertinoAlertDialog( - title: Text(AppLocalizations.of(context).no_name_title), - content: - Text(AppLocalizations.of(context).no_name_message), - actions: [ - CupertinoDialogAction( - child: Text(AppLocalizations.of(context).ok), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ); - return; - } - - List players = []; - for (var controller in _playerNameTextControllers) { - players.add(controller.text); - } - GameSession gameSession = GameSession( - createdAt: DateTime.now(), - gameTitle: _gameTitleTextController.text, - players: players, - pointLimit: Globals.pointLimit, - caboPenalty: Globals.caboPenalty, - isPointsLimitEnabled: selectedMode!, - ); - final index = await gameManager.addGameSession(gameSession); - if (context.mounted) { - Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (context) => ActiveGameView( - gameSession: gameManager.gameList[index]))); - } - }, - ), - ), - ], - )))); - } - - bool everyPlayerHasAName() { - for (var controller in _playerNameTextControllers) { - if (controller.text == '') { - return false; - } - } - return true; - } - - @override - void dispose() { - for (var controller in _playerNameTextControllers) { - controller.dispose(); - } - super.dispose(); - } -} diff --git a/lib/views/graph_view.dart b/lib/views/graph_view.dart deleted file mode 100644 index 39e91c2..0000000 --- a/lib/views/graph_view.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:cabo_counter/data/models/game_session.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:syncfusion_flutter_charts/charts.dart'; - -class GraphView extends StatefulWidget { - final GameSession gameSession; - - const GraphView({super.key, required this.gameSession}); - - @override - State createState() => _GraphViewState(); -} - -class _GraphViewState extends State { - /// List of colors for the graph lines. - List lineColors = [ - Colors.red, - Colors.blue, - Colors.orange.shade400, - Colors.purple, - Colors.green, - ]; - - @override - Widget build(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text(AppLocalizations.of(context).game_process), - previousPageTitle: AppLocalizations.of(context).back, - ), - child: Padding( - padding: const EdgeInsets.fromLTRB(0, 100, 0, 0), - child: SfCartesianChart( - legend: - const Legend(isVisible: true, position: LegendPosition.bottom), - primaryXAxis: const NumericAxis(), - primaryYAxis: const NumericAxis(), - series: getCumulativeScores(), - ), - ), - ); - } - - /// Returns a list of LineSeries representing the cumulative scores of each player. - /// Each series contains data points for each round, showing the cumulative score up to that round. - /// The x-axis represents the round number, and the y-axis represents the cumulative score. - List> getCumulativeScores() { - final rounds = widget.gameSession.roundList; - final playerCount = widget.gameSession.players.length; - final playerNames = widget.gameSession.players; - - List> cumulativeScores = List.generate(playerCount, (_) => []); - List runningTotals = List.filled(playerCount, 0); - - for (var round in rounds) { - for (int i = 0; i < playerCount; i++) { - runningTotals[i] += round.scores[i]; - cumulativeScores[i].add(runningTotals[i]); - } - } - - /// Create a list of LineSeries for each player - /// Each series contains data points for each round - return List.generate(playerCount, (i) { - final data = List.generate( - cumulativeScores[i].length, - (j) => (j + 1, cumulativeScores[i][j]), // (round, score) - ); - - /// Create a LineSeries for the player - /// The xValueMapper maps the round number, and the yValueMapper maps the cumulative score. - return LineSeries<(int, int), int>( - name: playerNames[i], - dataSource: data, - xValueMapper: (record, _) => record.$1, // Runde - yValueMapper: (record, _) => record.$2, // Punktestand - markerSettings: const MarkerSettings(isVisible: true), - color: lineColors[i], - ); - }); - } -} diff --git a/lib/views/information_view.dart b/lib/views/information_view.dart deleted file mode 100644 index 1d0918b..0000000 --- a/lib/views/information_view.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class InformationView extends StatelessWidget { - const InformationView({super.key}); - - @override - Widget build(BuildContext context) { - return CupertinoPageScaffold( - resizeToAvoidBottomInset: false, - navigationBar: CupertinoNavigationBar( - middle: Text(AppLocalizations.of(context).about), - ), - child: SafeArea( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 10, 0, 0), - child: Text( - AppLocalizations.of(context).app_name, - style: const TextStyle( - fontSize: 30, - fontWeight: FontWeight.bold, - ), - ), - ), - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 30), - child: SizedBox( - height: 200, - child: Image.asset('assets/cabo_counter-logo_rounded.png'), - )), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 30), - child: Text( - AppLocalizations.of(context).about_text, - textAlign: TextAlign.center, - softWrap: true, - )), - const SizedBox( - height: 30, - ), - const Text( - '\u00A9 Felix Kirchner', - style: TextStyle(fontSize: 16), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - onPressed: () => - launchUrl(Uri.parse('https://www.instagram.com/fx.kr')), - icon: const Icon(FontAwesomeIcons.instagram)), - IconButton( - onPressed: () => launchUrl( - Uri.parse('mailto:felix.kirchner.fk@gmail.com')), - icon: const Icon(CupertinoIcons.envelope)), - IconButton( - onPressed: () => - launchUrl(Uri.parse('https://www.github.com/flixcoo')), - icon: const Icon(FontAwesomeIcons.github)), - ], - ), - ], - ))); - } -} diff --git a/lib/views/main_menu_view.dart b/lib/views/main_menu_view.dart deleted file mode 100644 index 69783d8..0000000 --- a/lib/views/main_menu_view.dart +++ /dev/null @@ -1,233 +0,0 @@ -import 'package:cabo_counter/data/models/game_manager.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:cabo_counter/services/local_storage_service.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/utility/globals.dart'; -import 'package:cabo_counter/views/active_game_view.dart'; -import 'package:cabo_counter/views/create_game_view.dart'; -import 'package:cabo_counter/views/settings_view.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class MainMenuView extends StatefulWidget { - const MainMenuView({super.key}); - - @override - // ignore: library_private_types_in_public_api - _MainMenuViewState createState() => _MainMenuViewState(); -} - -class _MainMenuViewState extends State { - bool _isLoading = true; - - @override - initState() { - super.initState(); - LocalStorageService.loadGameSessions().then((_) { - setState(() { - _isLoading = false; - }); - }); - gameManager.addListener(_updateView); - } - - void _updateView() { - if (mounted) setState(() {}); - } - - @override - Widget build(BuildContext context) { - return ListenableBuilder( - listenable: gameManager, - builder: (context, _) { - return CupertinoPageScaffold( - resizeToAvoidBottomInset: false, - navigationBar: CupertinoNavigationBar( - leading: IconButton( - onPressed: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => const SettingsView(), - ), - ); - }, - icon: const Icon(CupertinoIcons.settings, size: 30)), - middle: const Text('Cabo Counter'), - trailing: IconButton( - onPressed: () => { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => const CreateGame(), - ), - ) - }, - icon: const Icon(CupertinoIcons.add)), - ), - child: CupertinoPageScaffold( - child: SafeArea( - child: _isLoading - ? const Center(child: CupertinoActivityIndicator()) - : gameManager.gameList.isEmpty - ? Column( - mainAxisAlignment: - MainAxisAlignment.center, // Oben ausrichten - children: [ - const SizedBox(height: 30), // Abstand von oben - Center( - child: GestureDetector( - onTap: () => setState(() {}), - child: Icon( - CupertinoIcons.plus, - size: 60, - color: CustomTheme.primaryColor, - ), - )), - const SizedBox(height: 10), // Abstand von oben - const Padding( - padding: EdgeInsets.symmetric(horizontal: 70), - child: Text( - 'Ganz schön leer hier...\nFüge über den Button oben rechts eine neue Runde hinzu.', - textAlign: TextAlign.center, - style: TextStyle(fontSize: 16), - ), - ), - ], - ) - : ListView.builder( - itemCount: gameManager.gameList.length, - itemBuilder: (context, index) { - final session = gameManager.gameList[index]; - return ListenableBuilder( - listenable: session, - builder: (context, _) { - return Dismissible( - key: Key(session.gameTitle), - background: Container( - color: CupertinoColors.destructiveRed, - alignment: Alignment.centerLeft, - padding: - const EdgeInsets.only(left: 20.0), - child: const Icon( - CupertinoIcons.delete, - color: CupertinoColors.white, - ), - ), - direction: DismissDirection.startToEnd, - confirmDismiss: (direction) async { - final String gameTitle = gameManager - .gameList[index].gameTitle; - return await _showDeleteGamePopup( - gameTitle); - }, - onDismissed: (direction) { - gameManager.removeGameSession(index); - }, - dismissThresholds: const { - DismissDirection.startToEnd: 0.6 - }, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 10.0), - child: CupertinoListTile( - backgroundColorActivated: - CustomTheme.backgroundColor, - title: Text(session.gameTitle), - subtitle: - session.isGameFinished == true - ? Text( - '\u{1F947} ${session.winner}', - style: const TextStyle( - fontSize: 14), - ) - : Text( - '${AppLocalizations.of(context).mode}: ${_translateGameMode(session.isPointsLimitEnabled)}', - style: const TextStyle( - fontSize: 14), - ), - trailing: Row( - children: [ - Text('${session.roundNumber}'), - const SizedBox(width: 3), - const Icon(CupertinoIcons - .arrow_2_circlepath_circle_fill), - const SizedBox(width: 15), - Text('${session.players.length}'), - const SizedBox(width: 3), - const Icon( - CupertinoIcons.person_2_fill), - ], - ), - onTap: () async { - //ignore: unused_local_variable - final val = await Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => - ActiveGameView( - gameSession: gameManager - .gameList[index]), - ), - ); - setState(() {}); - }, - ), - ), - ); - }); - }, - ), - ), - ), - ); - }); - } - - /// Translates the game mode boolean into the corresponding String. - /// If [pointLimit] is true, it returns '101 Punkte', otherwise it returns 'Unbegrenzt'. - String _translateGameMode(bool pointLimit) { - if (pointLimit) { - return '${Globals.pointLimit} ${AppLocalizations.of(context).points}'; - } - return AppLocalizations.of(context).unlimited; - } - - /// Shows a confirmation dialog to delete all game sessions. - /// Returns true if the user confirms the deletion, false otherwise. - /// [gameTitle] is the title of the game session to be deleted. - Future _showDeleteGamePopup(String gameTitle) async { - bool? shouldDelete = await showCupertinoDialog( - context: context, - builder: (context) { - return CupertinoAlertDialog( - title: Text(AppLocalizations.of(context).delete_game_title), - content: Text( - AppLocalizations.of(context).delete_game_message(gameTitle)), - actions: [ - CupertinoDialogAction( - onPressed: () { - Navigator.pop(context, false); - }, - child: Text(AppLocalizations.of(context).cancel), - ), - CupertinoDialogAction( - onPressed: () { - Navigator.pop(context, true); - }, - child: Text(AppLocalizations.of(context).delete), - ), - ], - ); - }, - ) ?? - false; - return shouldDelete; - } - - @override - void dispose() { - gameManager.removeListener(_updateView); - super.dispose(); - } -} diff --git a/lib/views/mode_selection_view.dart b/lib/views/mode_selection_view.dart deleted file mode 100644 index 93cdc7a..0000000 --- a/lib/views/mode_selection_view.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:flutter/cupertino.dart'; - -class ModeSelectionMenu extends StatelessWidget { - final int pointLimit; - const ModeSelectionMenu({super.key, required this.pointLimit}); - - @override - Widget build(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text(AppLocalizations.of(context).select_game_mode), - ), - child: ListView( - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), - child: CupertinoListTile( - title: Text('$pointLimit ${AppLocalizations.of(context).points}', - style: CustomTheme.modeTitle), - subtitle: Text( - AppLocalizations.of(context) - .point_limit_description(pointLimit), - style: CustomTheme.modeDescription, - maxLines: 3, - ), - onTap: () { - Navigator.pop(context, true); - }, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: CupertinoListTile( - title: Text(AppLocalizations.of(context).unlimited, - style: CustomTheme.modeTitle), - subtitle: Text( - AppLocalizations.of(context).unlimited_description, - style: CustomTheme.modeDescription, - maxLines: 3, - ), - onTap: () { - Navigator.pop(context, false); - }, - ), - ), - ], - ), - ); - } -} diff --git a/lib/views/round_view.dart b/lib/views/round_view.dart deleted file mode 100644 index 4ad8f0d..0000000 --- a/lib/views/round_view.dart +++ /dev/null @@ -1,408 +0,0 @@ -import 'package:cabo_counter/data/models/game_session.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:cabo_counter/services/local_storage_service.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; - -class RoundView extends StatefulWidget { - final GameSession gameSession; - final int roundNumber; - const RoundView( - {super.key, required this.roundNumber, required this.gameSession}); - - @override - // ignore: library_private_types_in_public_api - _RoundViewState createState() => _RoundViewState(); -} - -class _RoundViewState extends State { - /// The current game session. - late GameSession gameSession = widget.gameSession; - - /// Index of the player who said CABO. - int _caboPlayerIndex = 0; - - /// Index of the player who has Kamikaze. - /// Default is null (no Kamikaze player). - int? _kamikazePlayerIndex; - - /// List of text controllers for the score text fields. - late final List _scoreControllerList = List.generate( - widget.gameSession.players.length, - (index) => TextEditingController(), - ); - - /// List of focus nodes for the score text fields. - late final List _focusNodeList = List.generate( - widget.gameSession.players.length, - (index) => FocusNode(), - ); - - @override - void initState() { - print('=== Runde ${widget.roundNumber} geöffnet ==='); - if (widget.roundNumber < widget.gameSession.roundNumber || - widget.gameSession.isGameFinished == true) { - print( - 'Diese wurde bereits gespielt, deshalb werden die alten Punktestaende angezeigt'); - - // If the current round has already been played, the text fields - // are filled with the scores from this round - for (int i = 0; i < _scoreControllerList.length; i++) { - _scoreControllerList[i].text = - gameSession.roundList[widget.roundNumber - 1].scores[i].toString(); - } - _caboPlayerIndex = - gameSession.roundList[widget.roundNumber - 1].caboPlayerIndex; - _kamikazePlayerIndex = - gameSession.roundList[widget.roundNumber - 1].kamikazePlayerIndex; - } - - super.initState(); - } - - @override - @override - Widget build(BuildContext context) { - final bottomInset = MediaQuery.of(context).viewInsets.bottom; - - return CupertinoPageScaffold( - resizeToAvoidBottomInset: false, - navigationBar: CupertinoNavigationBar( - transitionBetweenRoutes: true, - middle: Text(AppLocalizations.of(context).results), - leading: CupertinoButton( - padding: EdgeInsets.zero, - onPressed: () => { - LocalStorageService.saveGameSessions(), - Navigator.pop(context, widget.gameSession) - }, - child: Text(AppLocalizations.of(context).cancel), - ), - ), - child: Stack( - children: [ - Positioned.fill( - child: SingleChildScrollView( - padding: EdgeInsets.only(bottom: 100 + bottomInset), - child: SafeArea( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 40), - Text( - '${AppLocalizations.of(context).round} ${widget.roundNumber}', - style: CustomTheme.roundTitle), - const SizedBox(height: 10), - Text( - AppLocalizations.of(context).who_said_cabo, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - Padding( - padding: EdgeInsets.symmetric( - horizontal: - widget.gameSession.players.length > 3 ? 5 : 20, - vertical: 10, - ), - child: SizedBox( - height: 40, - child: CupertinoSegmentedControl( - unselectedColor: CustomTheme.backgroundTintColor, - selectedColor: CustomTheme.primaryColor, - groupValue: _caboPlayerIndex, - children: Map.fromEntries(widget.gameSession.players - .asMap() - .entries - .map((entry) { - final index = entry.key; - final name = entry.value; - return MapEntry( - index, - Padding( - padding: EdgeInsets.symmetric( - horizontal: widget.gameSession - .getLengthOfPlayerNames() > - 20 - ? (widget.gameSession - .getLengthOfPlayerNames() > - 32 - ? 5 - : 10) - : 15, - vertical: 6, - ), - child: Text( - name, - textAlign: TextAlign.center, - maxLines: 1, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: widget.gameSession - .getLengthOfPlayerNames() > - 28 - ? 14 - : 18, - ), - ), - ), - ); - })), - onValueChanged: (value) { - setState(() { - _caboPlayerIndex = value; - }); - }, - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: CupertinoListTile( - title: Text(AppLocalizations.of(context).player), - trailing: Row( - children: [ - SizedBox( - width: 100, - child: Center( - child: Text( - AppLocalizations.of(context).points))), - const SizedBox(width: 20), - SizedBox( - width: 80, - child: Center( - child: Text(AppLocalizations.of(context) - .kamikaze))), - ], - ), - ), - ), - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: widget.gameSession.players.length, - itemBuilder: (context, index) { - final name = widget.gameSession.players[index]; - return Padding( - padding: const EdgeInsets.symmetric( - vertical: 10, horizontal: 20), - child: ClipRRect( - borderRadius: BorderRadius.circular(12), - child: CupertinoListTile( - backgroundColor: CupertinoColors.secondaryLabel, - title: Row(children: [Text(name)]), - subtitle: Text( - '${widget.gameSession.playerScores[index]}' - ' ${AppLocalizations.of(context).points}'), - trailing: Row( - children: [ - SizedBox( - width: 100, - child: CupertinoTextField( - maxLength: 3, - focusNode: _focusNodeList[index], - keyboardType: - const TextInputType.numberWithOptions( - signed: true, - decimal: false, - ), - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - textInputAction: index == - widget.gameSession.players - .length - - 1 - ? TextInputAction.done - : TextInputAction.next, - controller: _scoreControllerList[index], - placeholder: - AppLocalizations.of(context).points, - textAlign: TextAlign.center, - onSubmitted: (_) => - _focusNextTextfield(index), - onChanged: (_) => setState(() {}), - ), - ), - const SizedBox(width: 50), - GestureDetector( - onTap: () { - setState(() { - _kamikazePlayerIndex = - (_kamikazePlayerIndex == index) - ? null - : index; - }); - }, - child: Container( - width: 24, - height: 24, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: _kamikazePlayerIndex == index - ? CupertinoColors.systemRed - : CupertinoColors - .tertiarySystemFill, - border: Border.all( - color: _kamikazePlayerIndex == index - ? CupertinoColors.systemRed - : CupertinoColors.systemGrey, - ), - ), - child: _kamikazePlayerIndex == index - ? const Icon( - CupertinoIcons.exclamationmark, - size: 16, - color: CupertinoColors.white, - ) - : null, - ), - ), - const SizedBox(width: 22), - ], - ), - ), - ), - ); - }, - ), - ], - ), - ), - ), - ), - Positioned( - left: 0, - right: 0, - bottom: bottomInset, - child: KeyboardVisibilityBuilder(builder: (context, visible) { - if (!visible) { - return Container( - height: 80, - padding: const EdgeInsets.only(bottom: 20), - color: CustomTheme.backgroundTintColor, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - CupertinoButton( - onPressed: _areRoundInputsValid() - ? () { - _finishRound(); - LocalStorageService.saveGameSessions(); - Navigator.pop(context, widget.gameSession); - } - : null, - child: Text(AppLocalizations.of(context).done), - ), - CupertinoButton( - onPressed: _areRoundInputsValid() - ? () { - _finishRound(); - LocalStorageService.saveGameSessions(); - if (widget.gameSession.isGameFinished == true) { - Navigator.pop(context, widget.gameSession); - } else { - Navigator.of(context, rootNavigator: true) - .pushReplacement( - CupertinoPageRoute( - builder: (context) => RoundView( - gameSession: widget.gameSession, - roundNumber: widget.roundNumber + 1, - ), - ), - ); - } - } - : null, - child: Text(AppLocalizations.of(context).next_round), - ), - ], - ), - ); - } else { - return const SizedBox.shrink(); - } - }), - ) - ], - ), - ); - } - - /// Focuses the next text field in the list of text fields. - /// [index] is the index of the current text field. - void _focusNextTextfield(int index) { - if (index < widget.gameSession.players.length - 1) { - FocusScope.of(context).requestFocus(_focusNodeList[index + 1]); - } else { - _focusNodeList[index].unfocus(); - } - } - - /// Checks if the inputs for the round are valid. - /// Returns true if the inputs are valid, false otherwise. - /// Round Inputs are valid if every player has a score or - /// kamikaze is selected for a player - bool _areRoundInputsValid() { - if (_areTextFieldsEmpty() && _kamikazePlayerIndex == null) return false; - return true; - } - - /// Checks if any of the text fields for the players points are empty. - /// Returns true if any of the text fields is empty, false otherwise. - bool _areTextFieldsEmpty() { - for (TextEditingController t in _scoreControllerList) { - if (t.text.isEmpty) { - return true; - } - } - return false; - } - - /// Finishes the current round. - /// It first determines, ifCalls the [_calculateScoredPoints()] method to calculate the points for - /// every player. If the round is the highest round played in this game, - /// it expands the player score lists. At the end it updates the score - /// array for the game. - void _finishRound() { - print('===================================='); - print('Runde ${widget.roundNumber} beendet'); - // The shown round is smaller than the newest round - if (widget.roundNumber < widget.gameSession.roundNumber) { - print('Da diese Runde bereits gespielt wurde, werden die alten ' - 'Punktestaende ueberschrieben'); - } - if (_kamikazePlayerIndex != null) { - print('${widget.gameSession.players[_kamikazePlayerIndex!]} hat Kamikaze ' - 'und bekommt 0 Punkte'); - print('Alle anderen Spieler bekommen 50 Punkte'); - widget.gameSession - .applyKamikaze(widget.roundNumber, _kamikazePlayerIndex!); - } else { - List roundScores = []; - for (TextEditingController c in _scoreControllerList) { - if (c.text.isNotEmpty) roundScores.add(int.parse(c.text)); - } - widget.gameSession.calculateScoredPoints( - widget.roundNumber, roundScores, _caboPlayerIndex); - } - widget.gameSession.updatePoints(); - if (widget.gameSession.isGameFinished == true) { - print('Das Spiel ist beendet'); - } else if (widget.roundNumber == widget.gameSession.roundNumber) { - widget.gameSession.increaseRound(); - } - } - - @override - void dispose() { - for (final controller in _scoreControllerList) { - controller.dispose(); - } - for (final focusNode in _focusNodeList) { - focusNode.dispose(); - } - super.dispose(); - } -} diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart deleted file mode 100644 index 53eb92d..0000000 --- a/lib/views/settings_view.dart +++ /dev/null @@ -1,239 +0,0 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:cabo_counter/services/config_service.dart'; -import 'package:cabo_counter/services/local_storage_service.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/utility/globals.dart'; -import 'package:cabo_counter/widgets/stepper.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:package_info_plus/package_info_plus.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class SettingsView extends StatefulWidget { - const SettingsView({super.key}); - - @override - State createState() => _SettingsViewState(); -} - -class _SettingsViewState extends State { - UniqueKey _stepperKey1 = UniqueKey(); - UniqueKey _stepperKey2 = UniqueKey(); - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text(AppLocalizations.of(context).settings), - ), - child: SafeArea( - child: Stack( - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).points, - style: CustomTheme.rowTitle, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(15, 10, 10, 0), - child: CupertinoListTile( - padding: EdgeInsets.zero, - title: Text(AppLocalizations.of(context).cabo_penalty), - subtitle: Text( - AppLocalizations.of(context).cabo_penalty_subtitle), - trailing: Stepper( - key: _stepperKey1, - initialValue: Globals.caboPenalty, - minValue: 0, - maxValue: 50, - step: 1, - onChanged: (newCaboPenalty) { - setState(() { - ConfigService.setCaboPenalty(newCaboPenalty); - Globals.caboPenalty = newCaboPenalty; - }); - }, - ), - )), - Padding( - padding: const EdgeInsets.fromLTRB(15, 10, 10, 0), - child: CupertinoListTile( - padding: EdgeInsets.zero, - title: Text(AppLocalizations.of(context).point_limit), - subtitle: - Text(AppLocalizations.of(context).point_limit_subtitle), - trailing: Stepper( - key: _stepperKey2, - initialValue: Globals.pointLimit, - minValue: 30, - maxValue: 1000, - step: 10, - onChanged: (newPointLimit) { - setState(() { - ConfigService.setPointLimit(newPointLimit); - Globals.pointLimit = newPointLimit; - }); - }, - ), - )), - Padding( - padding: const EdgeInsets.fromLTRB(0, 10, 0, 0), - child: Center( - heightFactor: 0.9, - child: CupertinoButton( - padding: EdgeInsets.zero, - onPressed: () => setState(() { - ConfigService.resetConfig(); - _stepperKey1 = UniqueKey(); - _stepperKey2 = UniqueKey(); - }), - child: - Text(AppLocalizations.of(context).reset_to_default), - ), - )), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).game_data, - style: CustomTheme.rowTitle, - ), - ), - Padding( - padding: const EdgeInsets.only(top: 30), - child: Center( - heightFactor: 1, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CupertinoButton( - color: CustomTheme.primaryColor, - sizeStyle: CupertinoButtonSize.medium, - child: Text( - AppLocalizations.of(context).import_data, - style: - TextStyle(color: CustomTheme.backgroundColor), - ), - onPressed: () async { - final success = - await LocalStorageService.importJsonFile(); - if (!success && context.mounted) { - showCupertinoDialog( - context: context, - builder: (context) => CupertinoAlertDialog( - title: Text( - AppLocalizations.of(context) - .error), - content: Text( - AppLocalizations.of(context) - .error_import), - actions: [ - CupertinoDialogAction( - child: Text( - AppLocalizations.of(context) - .ok), - onPressed: () => - Navigator.pop(context), - ), - ], - )); - } - }), - const SizedBox( - width: 20, - ), - CupertinoButton( - color: CustomTheme.primaryColor, - sizeStyle: CupertinoButtonSize.medium, - child: Text( - AppLocalizations.of(context).export_data, - style: - TextStyle(color: CustomTheme.backgroundColor), - ), - onPressed: () async { - final success = - await LocalStorageService.exportJsonFile(); - if (!success && context.mounted) { - showCupertinoDialog( - context: context, - builder: (context) => CupertinoAlertDialog( - title: - Text(AppLocalizations.of(context).error), - content: Text(AppLocalizations.of(context) - .error_export), - actions: [ - CupertinoDialogAction( - child: - Text(AppLocalizations.of(context).ok), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ); - } - }, - ), - ], - )), - ) - ], - ), - Positioned( - bottom: 30, - left: 0, - right: 0, - child: Column( - children: [ - Center( - child: Text(AppLocalizations.of(context).error_found), - ), - Padding( - padding: const EdgeInsets.fromLTRB(0, 0, 0, 30), - child: Center( - child: CupertinoButton( - onPressed: () => launchUrl(Uri.parse( - 'https://github.com/flixcoo/Cabo-Counter/issues')), - child: Text(AppLocalizations.of(context).create_issue), - ), - ), - ), - FutureBuilder( - future: _getPackageInfo(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text( - '${Globals.appDevPhase} ${snapshot.data!.version} ' - '(${AppLocalizations.of(context).build} ${snapshot.data!.buildNumber})', - textAlign: TextAlign.center, - ); - } else if (snapshot.hasError) { - return Text( - '${AppLocalizations.of(context).app_version} -.-.- (${AppLocalizations.of(context).build} -)', - textAlign: TextAlign.center, - ); - } - return Text( - AppLocalizations.of(context).load_version, - textAlign: TextAlign.center, - ); - }, - ) - ], - )), - ], - )), - ); - } - - Future _getPackageInfo() async { - return await PackageInfo.fromPlatform(); - } -} diff --git a/pubspec.yaml b/pubspec.yaml index ac80f69..322a508 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.3.2+245 +version: 0.5.8+674 environment: sdk: ^3.5.4 @@ -26,6 +26,13 @@ dependencies: sdk: flutter intl: any syncfusion_flutter_charts: ^30.1.37 + uuid: ^4.5.1 + rate_my_app: ^2.3.2 + reorderables: ^0.4.2 + collection: ^1.18.0 + confetti: ^0.6.0 + flutter_oss_licenses: ^2.0.1 + google_fonts: ^6.3.0 drift: ^2.26.1 drift_flutter: ^0.2.4 @@ -42,4 +49,5 @@ flutter: uses-material-design: false assets: - assets/cabo_counter-logo_rounded.png - - assets/schema.json + - assets/game_list-schema.json + - assets/game-schema.json diff --git a/test/data/game_session_test.dart b/test/data/game_session_test.dart index a28a4e8..89c78ad 100644 --- a/test/data/game_session_test.dart +++ b/test/data/game_session_test.dart @@ -9,6 +9,7 @@ void main() { setUp(() { session = GameSession( + id: '1', createdAt: testDate, gameTitle: testTitle, players: testPlayers, @@ -61,11 +62,6 @@ void main() { }); group('Helper Functions', () { - test('getLengthOfPlayerNames', () { - expect(session.getLengthOfPlayerNames(), - equals(15)); // Alice(5) + Bob(3) + Charlie(7) - }); - test('increaseRound', () { expect(session.roundNumber, 1); session.increaseRound(); @@ -115,15 +111,15 @@ void main() { expect(session.roundList[0].caboPlayerIndex, 0); }); - test('updatePoints - game not finished', () async { + test('updatePoints - game not finished', () { session.addRoundScoresToList(1, [10, 20, 30], [10, 20, 30], 0); - await session.updatePoints(); + session.updatePoints(); expect(session.isGameFinished, isFalse); }); - test('updatePoints - game finished', () async { + test('updatePoints - game finished', () { session.addRoundScoresToList(1, [101, 20, 30], [101, 20, 30], 0); - await session.updatePoints(); + session.updatePoints(); expect(session.isGameFinished, isTrue); }); @@ -155,9 +151,9 @@ void main() { expect(session.playerScores, equals([50, 0, 30])); }); - test('_setWinner via updatePoints', () async { + test('_setWinner via updatePoints', () { session.addRoundScoresToList(1, [101, 20, 30], [101, 0, 30], 1); - await session.updatePoints(); + session.updatePoints(); expect(session.winner, 'Bob'); // Bob has lowest score (20) }); });