diff --git a/.cirrus.yml b/.cirrus.yml
deleted file mode 100644
index b5c29eca..00000000
--- a/.cirrus.yml
+++ /dev/null
@@ -1,112 +0,0 @@
-timeout_in: 30m
-env:
- FLUTTER_CHANNEL: stable
- FLUTTER_VERSION: v1.12.13+hotfix.5
- DOCKER_VERSION: v1.12.13-hotfix.5 # docker tags cannot have plus
-
-task:
- # this task should fail fast or rely on 'depends_on' for all other tasks
- name: Static analysis, formatting, and unit tests
- container:
- image: cirrusci/flutter:$DOCKER_VERSION
- pub_cache:
- folder: ~/.pub-cache
- activate_coverage_script: pub global activate coverage
- tests_script: ./scripts/runTests.sh
-
-task:
- name: Integration Tests for $app_arch (Linux)
- # don't run for PRs
- only_if: $CIRRUS_PR == ''
- skip: '!changesInclude(".cirrus.yml", "$app_arch/*", "$app_arch/**/*")'
- env:
- EMULATOR_API_LEVEL: 28
- ANDROID_ABI: "default;x86"
- matrix:
- app_arch: bloc_flutter
- app_arch: bloc_library
- app_arch: built_redux
- app_arch: firestore_redux
- app_arch: frideos_library
- app_arch: inherited_widget
- app_arch: mobx
- app_arch: mvc
- app_arch: mvi_flutter
- app_arch: mvu
- app_arch: change_notifier_provider
- app_arch: redux
- app_arch: scoped_model
- app_arch: simple_bloc_flutter
- app_arch: vanilla
- app_arch: states_rebuilder
- container:
- image: cirrusci/flutter:$DOCKER_VERSION
- cpu: 4
- memory: 10G
- kvm: true
- fix_kvm_script: sudo chown cirrus:cirrus /dev/kvm
- install_images_script: sdkmanager "system-images;android-$EMULATOR_API_LEVEL;$ANDROID_ABI"
- create_device_script:
- echo no | avdmanager create avd --force -n test -k "system-images;android-$EMULATOR_API_LEVEL;$ANDROID_ABI"
- start_emulator_background_script:
- $ANDROID_HOME/emulator/emulator-headless -verbose -avd test -no-audio -no-window
- pub_cache:
- folder: ~/.pub-cache
- wait_for_emulator_script:
- - ./scripts/android-wait-for-emulator.sh
-# - adb shell input keyevent 82
- doctor_script: flutter doctor -v
- devices_script: flutter devices
- ci_script: ./scripts/ci.sh ./$app_arch || ./scripts/ci.sh ./$app_arch
-
-task:
- name: Integration Tests for $app_arch (macOS)
- # don't run for PRs
- only_if: $CIRRUS_PR == ''
- skip: '!changesInclude(".cirrus.yml", "$app_arch/*", "$app_arch/**/*")'
- env:
- matrix:
- app_arch: bloc_flutter
- app_arch: bloc_library
- app_arch: built_redux
- app_arch: firestore_redux
- app_arch: frideos_library
- app_arch: inherited_widget
- app_arch: mvc
- app_arch: mvi_flutter
- app_arch: mvu
- app_arch: change_notifier_provider
- app_arch: redux
- app_arch: scoped_model
- app_arch: simple_bloc_flutter
- app_arch: vanilla
- app_arch: states_rebuilder
- osx_instance:
- image: mojave-xcode-11.2.1-flutter
- simulator_script:
- - xcrun simctl list devicetypes
- - xcrun simctl list runtimes
- # create simulator
- - udid=$(xcrun simctl create "iPhone X" com.apple.CoreSimulator.SimDeviceType.iPhone-X com.apple.CoreSimulator.SimRuntime.iOS-13-2)
- # boot simulator
- - xcrun simctl boot $udid
- pin_flutter_script:
-# - sudo gem install cocoapods # missing from mojave-xcode-11.2.1-flutter (not found)
- - flutter --version
- # pin to ${FLUTTER_VERSION}
- - wget --quiet --output-document=flutter.zip https://storage.googleapis.com/flutter_infra/releases/${FLUTTER_CHANNEL}/macos/flutter_macos_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.zip && unzip -qq flutter.zip > /dev/null && rm flutter.zip
- - export PATH="$PATH":"$HOME/.pub-cache/bin"
- - export PATH=$PWD/flutter/bin:$PWD/flutter/bin/cache/dart-sdk/bin:$PATH
- - flutter --version
- doctor_script:
- - export PATH="$PATH":"$HOME/.pub-cache/bin"
- - export PATH=$PWD/flutter/bin:$PWD/flutter/bin/cache/dart-sdk/bin:$PATH
- - flutter doctor -v
- devices_script:
- - export PATH="$PATH":"$HOME/.pub-cache/bin"
- - export PATH=$PWD/flutter/bin:$PWD/flutter/bin/cache/dart-sdk/bin:$PATH
- - flutter devices
- ci_script:
- - export PATH="$PATH":"$HOME/.pub-cache/bin"
- - export PATH=$PWD/flutter/bin:$PWD/flutter/bin/cache/dart-sdk/bin:$PATH
- - ./scripts/ci.sh ./$app_arch || ./scripts/ci.sh ./$app_arch
diff --git a/.fvmrc b/.fvmrc
new file mode 100644
index 00000000..2bb4682a
--- /dev/null
+++ b/.fvmrc
@@ -0,0 +1,3 @@
+{
+ "flutter": "3.35.2"
+}
\ No newline at end of file
diff --git a/.github/actions/dart_analysis_and_tests/action.yml b/.github/actions/dart_analysis_and_tests/action.yml
new file mode 100644
index 00000000..5120e7f3
--- /dev/null
+++ b/.github/actions/dart_analysis_and_tests/action.yml
@@ -0,0 +1,43 @@
+name: "Validate"
+description: "Runs lint, format, and test on an app"
+inputs:
+ working-directory:
+ description: "Directory to run validation in"
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Set up flutter
+ uses: kuhnroyal/flutter-fvm-config-action/setup@v3
+
+ - name: Get dependencies
+ shell: bash
+ run: dart pub get
+ working-directory: ${{ inputs.working-directory }}
+
+ - name: Check dart formatting
+ shell: bash
+ run: dart format -o none --set-exit-if-changed .
+ working-directory: ${{ inputs.working-directory }}
+
+ - name: Check dart analysis
+ shell: bash
+ run: dart analyze --fatal-infos --fatal-warnings .
+ working-directory: ${{ inputs.working-directory }}
+
+ - name: Run unit tests and prepare coverage
+ shell: bash
+ run: |
+ dart pub global activate coverage
+ dart run test --coverage=coverage
+ dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on=lib
+ # Extract directory name for artifact naming
+ echo "DIR_NAME=$(basename "${{ inputs.working-directory }}")" >> $GITHUB_ENV
+ working-directory: ${{ inputs.working-directory }}
+
+ - name: Upload coverage artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-lcov-${{ env.DIR_NAME }}
+ path: ${{ inputs.working-directory }}/coverage/lcov.info
diff --git a/.github/actions/flutter_analysis_test_build/action.yml b/.github/actions/flutter_analysis_test_build/action.yml
new file mode 100644
index 00000000..188e03a1
--- /dev/null
+++ b/.github/actions/flutter_analysis_test_build/action.yml
@@ -0,0 +1,93 @@
+name: "Validate"
+description: "Runs lint, format, and test on an app"
+inputs:
+ working-directory:
+ description: "Directory to run validation in"
+ required: true
+ run-integration-tests:
+ description: "Run integration tests"
+ required: false
+ default: "true"
+ deploy-to-netlify:
+ description: "Deploy to Netlify"
+ required: true
+ default: "false"
+ netlify-auth-token:
+ description: "Netlify auth token"
+ required: true
+ netlify-site-id:
+ description: "Netlify site id"
+ required: true
+runs:
+ using: "composite"
+ steps:
+ - name: Set up flutter
+ uses: kuhnroyal/flutter-fvm-config-action/setup@v3
+
+ - name: Get dependencies
+ shell: bash
+ run: flutter pub get
+ working-directory: ${{ inputs.working-directory }}
+
+ - name: Check dart formatting
+ shell: bash
+ run: dart format -o none --set-exit-if-changed .
+ working-directory: ${{ inputs.working-directory }}
+
+ - name: Check dart analysis
+ shell: bash
+ run: dart analyze --fatal-infos --fatal-warnings .
+ working-directory: ${{ inputs.working-directory }}
+
+ - name: Run unit tests and prepare coverage
+ shell: bash
+ if: '[ -d "${{ inputs.working-directory }}/test" ]'
+ run: |
+ flutter test --coverage
+ # Extract directory name for artifact naming
+ echo "DIR_NAME=$(basename "${{ inputs.working-directory }}")" >> $GITHUB_ENV
+ working-directory: ${{ inputs.working-directory }}
+
+ - name: Upload coverage artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-lcov-${{ env.DIR_NAME }}
+ path: ${{ inputs.working-directory }}/coverage/lcov.info
+
+ - name: Run linux integration tests
+ if: ${{ inputs.run-integration-tests != 'false' }}
+ shell: bash
+ run: |
+ sudo apt-get update
+ sudo apt-get install \
+ clang \
+ cmake \
+ git \
+ ninja-build \
+ pkg-config \
+ libgtk-3-dev \
+ liblzma-dev \
+ libstdc++-12-dev \
+ libglu1-mesa \
+ xvfb
+ export DISPLAY=:99 # Set display for Xvfb
+ xvfb-run -a flutter test integration_test/app_test.dart -d linux
+ working-directory: ${{ inputs.working-directory }}
+
+ - name: Build web app
+ if: ${{ inputs.deploy-to-netlify != 'false' }}
+ shell: bash
+ run: |
+ flutter build web --wasm
+ working-directory: ${{ inputs.working-directory }}
+
+ - name: Deploy to Netlify
+ if: ${{ inputs.deploy-to-netlify != 'false' }}
+ uses: nwtgck/actions-netlify@v3.0
+ with:
+ publish-dir: ${{ inputs.working-directory }}/build/web
+ production-deploy: true
+ deploy-message: "Deploy from GitHub Actions"
+ env:
+ NETLIFY_AUTH_TOKEN: ${{ inputs.netlify-auth-token }}
+ NETLIFY_SITE_ID: ${{ inputs.netlify-site-id }}
diff --git a/.github/workflows/analyze_test_build.yml b/.github/workflows/analyze_test_build.yml
new file mode 100644
index 00000000..a5a90740
--- /dev/null
+++ b/.github/workflows/analyze_test_build.yml
@@ -0,0 +1,295 @@
+name: Static Analysis, Run Tests and Build Web Apps
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+
+jobs:
+ bloc_flutter:
+ name: bloc_flutter
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./bloc_flutter
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.BLOC_FLUTTER_NETLIFY_SITE_ID }}
+ bloc_library:
+ name: bloc_library
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./bloc_library
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.BLOC_LIBRARY_NETLIFY_SITE_ID }}
+ blocs:
+ name: blocs
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/dart_analysis_and_tests
+ with:
+ working-directory: ./blocs
+ change_notifier_provider:
+ name: change_notifier_provider
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./change_notifier_provider
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.CHANGE_NOTIFIER_PROVIDER_NETLIFY_SITE_ID }}
+ freezed_provider_value_notifier:
+ name: freezed_provider_value_notifier
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./freezed_provider_value_notifier
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.FREEZED_PROVIDER_VALUE_NOTIFIER_NETLIFY_SITE_ID }}
+ inherited_widget:
+ name: inherited_widget
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./inherited_widget
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.INHERITED_WIDGET_NETLIFY_SITE_ID }}
+ mobx:
+ name: mobx
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./mobx
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.MOBX_NETLIFY_SITE_ID }}
+ mvi_base:
+ name: mvi_base
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/dart_analysis_and_tests
+ with:
+ working-directory: ./mvi_base
+ mvi_flutter:
+ name: mvi_flutter
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./mvi_flutter
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.MVI_NETLIFY_SITE_ID }}
+ redux:
+ name: redux
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./redux
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.REDUX_NETLIFY_SITE_ID }}
+ scoped_model:
+ name: scoped_model
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./scoped_model
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.SCOPED_MODEL_NETLIFY_SITE_IT }}
+ signals:
+ name: signals
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./signals
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.SIGNALS_NETLIFY_SITE_ID }}
+ simple_bloc_flutter:
+ name: simple_bloc_flutter
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./simple_bloc_flutter
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.SIMPLE_BLOC_NETLIFY_SITE_ID }}
+ simple_blocs:
+ name: simple_blocs
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/dart_analysis_and_tests
+ with:
+ working-directory: ./simple_blocs
+ todos_repository_local_storage:
+ name: todos_repository_local_storage
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./todos_repository_local_storage
+ run-integration-tests: false
+ vanilla:
+ name: vanilla
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Static Analysis, Run Tests, and Build web app
+ uses: ./.github/actions/flutter_analysis_test_build
+ with:
+ working-directory: ./vanilla
+ deploy-to-netlify: ${{ github.ref_name == 'main' }}
+ netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+ netlify-site-id: ${{ secrets.VANILLA_NETLIFY_SITE_ID }}
+ combine_and_upload_coverage:
+ name: Combine and Upload Coverage
+ runs-on: ubuntu-latest
+ needs:
+ - bloc_flutter
+ - bloc_library
+ - blocs
+ - change_notifier_provider
+ - freezed_provider_value_notifier
+ - inherited_widget
+ - mobx
+ - mvi_base
+ - mvi_flutter
+ - redux
+ - scoped_model
+ - signals
+ - simple_bloc_flutter
+ - simple_blocs
+ - todos_repository_local_storage
+ - vanilla
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Download coverage artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: .
+ - name: Combine coverage files
+ run: |
+ combineCoverage() {
+ local artifact_dir=$1
+ local repo_dir=$2
+ # Extract the package directory path from the artifact name
+ # coverage-lcov-vanilla -> ./vanilla
+ local package_name=$(basename "$artifact_dir")
+ local package_dir="./${package_name#coverage-lcov-}"
+ escapedPath="$(echo $package_dir | sed 's/\//\\\//g')"
+
+ if [[ -d "$artifact_dir" ]]; then
+ # Find the lcov.info file in the artifact directory
+ for lcov_file in "$artifact_dir"/*.info; do
+ if [[ -f "$lcov_file" ]]; then
+ echo "Combining coverage from $package_dir"
+ # combine line coverage info from package tests to a common file
+ sed "s/^SF:lib/SF:$escapedPath\/lib/g" "$lcov_file" >> "$repo_dir/lcov.info"
+ break
+ fi
+ done
+ fi
+ }
+
+ # Initialize the combined coverage file
+ touch lcov.info
+
+ # Combine coverage from all downloaded artifacts
+ for artifact_dir in coverage-lcov-*/; do
+ if [[ -d "$artifact_dir" ]]; then
+ combineCoverage "$artifact_dir" "."
+ fi
+ done
+
+ echo "Combined coverage file created:"
+ ls -la lcov.info
+ echo "First few lines of combined coverage:"
+ head -10 lcov.info
+
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v5
+ with:
+ files: ./lcov.info
+ disable_search: true
+ env:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 34d26916..8bb94fa7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,3 +91,9 @@ lcov.info
!**/*/ios/**/default.pbxuser
!**/*/ios/**/default.perspectivev3
**/*/generated_plugin_registrant.dart
+
+.vscode/
+
+# FVM Version Cache
+.fvm/
+.cursor/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index d09dd464..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,352 +0,0 @@
-language: generic
-env:
- global:
- - FLUTTER_CHANNEL=stable
- - FLUTTER_VERSION=1.12.13+hotfix.5-${FLUTTER_CHANNEL}
- - API=28
- - ABI=x86
- - GOO=default
- - ANDROID_TOOLS=4333796 # android-28
- - ANDROID_HOME=${HOME}/android-sdk
- - GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/"
- - JDK="1.8" # the JDK used for running tests
- - TOOLS=${ANDROID_HOME}/tools
- # PATH order is incredibly important. e.g. the 'emulator' script exists in more than one place!
- - PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH}
- - FLUTTER_HOME=${HOME}/flutter
- - PATH=${HOME}/.pub-cache/bin:${PATH}
- - PATH=${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin:${PATH}
-
-jobs:
- include:
-
- - stage: Static analysis, formatting, and unit tests
- language: generic
- dist: bionic
- os: linux
- env: All unit and widget tests
- before_script:
- - sudo apt-get install -y --no-install-recommends lib32stdc++6 libstdc++6 > /dev/null
-
- # install pre-compiled flutter
- - wget --quiet --output-document=flutter.tar.xz https://storage.googleapis.com/flutter_infra/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_v${FLUTTER_VERSION}.tar.xz && tar xf flutter.tar.xz > /dev/null && rm flutter.tar.xz
- - export PATH="$PATH":"$HOME/.pub-cache/bin"
- - export PATH=$PWD/flutter/bin:$PWD/flutter/bin/cache/dart-sdk/bin:$PATH
- - flutter doctor -v
- - pub global activate coverage
- script: ./scripts/runTests.sh
- after_success: bash <(curl -s https://codecov.io/bash) -f lcov.info
- cache:
- directories:
- - $HOME/.pub-cache
-
- - &integration-test
- stage: Testing
- dist: bionic
- language: generic
- os: linux
- env: bloc_flutter_android
- # Run integration tests on android
- before_install: &before_install_linux
- - java -version
-
- # Set up KVM
- - sudo apt-get -y --no-install-recommends install bridge-utils libpulse0 libvirt-bin qemu-kvm virtinst ubuntu-vm-builder > /dev/null
- # add travis user to groups
- - sudo adduser $USER libvirt
- - sudo adduser $USER kvm
-
- # Set up JDK 8 for Android SDK
- - curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
- - export TARGET_JDK="${JDK}"
- - JDK="1.8" # used when running sdkmanager
- - source ~/.install-jdk-travis.sh
-
- # Set up Android SDK
- - wget -q "https://dl.google.com/android/repository/sdk-tools-linux-$ANDROID_TOOLS.zip" -O android-sdk-tools.zip
- - unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}
- - rm android-sdk-tools.zip
-
- # Avoid harmless sdkmanager warning
- - mkdir ~/.android
- - echo 'count=0' > ~/.android/repositories.cfg
-
- # Accept licenses before installing components, no need to echo y for each component
- - yes | sdkmanager --licenses >/dev/null
-
- # Download SDK tools
- - sdkmanager "platform-tools" >/dev/null
- - sdkmanager "tools" >/dev/null # A second time per Travis docs, gets latest versions
- - sdkmanager "build-tools;28.0.3" >/dev/null # Implicit gradle dependency - gradle drives changes
- - sdkmanager "platforms;android-$API" >/dev/null # We need the API of the emulator we will run
- - sdkmanager "platforms;android-28" >/dev/null # We need the API of the current compileSdkVersion from gradle.properties -
-
- - sdkmanager "emulator" >/dev/null
- - |
- if [[ $ABI =~ "arm" ]]; then
- # Download a pinned version of the emulator since default version can cause issues
- ${ANDROID_HOME}/emulator/emulator -version
- emulator_version=5264690 # 29.2.1.0 (build_id 5889189) ==> 28.0.23.0 (build_id 5264690)
- # sudo apt-get install -y libunwind8 libc++1
- curl -fo emulator.zip "https://dl.google.com/android/repository/emulator-linux-$emulator_version.zip"
- rm -rf "${ANDROID_HOME}/emulator"
- unzip -q emulator.zip -d "${ANDROID_HOME}"
- rm -f emulator.zip
- # install build tools and platforms for arm (to allow emulator to run)
- sdkmanager "build-tools;25.0.2" "platforms;android-25" > /dev/null
- fi
- - ${ANDROID_HOME}/emulator/emulator -version
-
- - sdkmanager "extras;android;m2repository" >/dev/null
- - sdkmanager "system-images;android-$API;$GOO;$ABI" >/dev/null # install system images for emulator
-
- # Create an Android emulator
- # - echo no | avdmanager --verbose create avd --force -n test -k "system-images;android-$API;$GOO;$ABI" -c 10M
- - echo no | avdmanager --verbose create avd --force -n test -k "system-images;android-$API;$GOO;$ABI"
- # - EMU_PARAMS="
- # -verbose
- # -no-snapshot
- # -no-window
- # -no-audio
- # -no-boot-anim
- # -camera-back none
- # -camera-front none
- # -selinux permissive
- # -qemu -m 2048"
- - EMU_PARAMS="
- -avd test
- -verbose
- -no-window
- -no-audio
- "
- - EMU_COMMAND="emulator"
- - |
- if [[ $ABI =~ "x86" ]]; then
- EMU_COMMAND="emulator"
- else
- # emulate graphics if running on ARM
- EMU_PARAMS="${EMU_PARAMS} -gpu swiftshader"
- fi
- # This double "sudo" monstrosity is used to have Travis execute the
- # emulator with its new group permissions and help preserve the rule
- # of least privilege.
- - sudo -E sudo -u $USER -E bash -c "${ANDROID_HOME}/emulator/${EMU_COMMAND} ${EMU_PARAMS} &"
-
- # install flutter (while emulator is starting)
- - sudo apt-get install -y --no-install-recommends lib32stdc++6 libstdc++6 > /dev/null
- - wget --quiet --output-document=flutter.tar.xz https://storage.googleapis.com/flutter_infra/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_v${FLUTTER_VERSION}.tar.xz
- - tar xf flutter.tar.xz -C $(dirname ${FLUTTER_HOME})
- - rm flutter.tar.xz
-
- # Switch back to our target JDK version to build and run tests
- - JDK="${TARGET_JDK}"
- - source ~/.install-jdk-travis.sh
-
- - flutter doctor -v
-
- # wait for emulator to finish startup
- - ./scripts/android-wait-for-emulator.sh
- # unlock screen
- - adb shell input keyevent 82 &
-
- script: travis_retry ./scripts/ci.sh ./bloc_flutter
- cache:
- directories:
- - $HOME/.pub-cache
- - $HOME/.gradle
-
-# - <<: *integration-test
- - stage: Testing
- os: osx
-# osx_image: xcode9.4
-# osx_image: xcode9.2
- osx_image: xcode11.3 # for firebase
- env: bloc_flutter_ios
-# env: vanilla_ios
- # Run integration tests on ios
- before_install: &before_install_osx
-# - xcrun simctl list devicetypes
-# - xcrun simctl list runtimes
-# # create simulator
-# - udid=$(xcrun simctl create "iPhone X" com.apple.CoreSimulator.SimDeviceType.iPhone-X com.apple.CoreSimulator.SimRuntime.iOS-11-2)
-# # boot simulator
-# - xcrun simctl boot $udid
-# - xcrun simctl list
- - xcrun simctl create "iPhone X" com.apple.CoreSimulator.SimDeviceType.iPhone-X com.apple.CoreSimulator.SimRuntime.iOS-13-3 | xargs xcrun simctl boot
- # skip homebrew update
- - export HOMEBREW_NO_AUTO_UPDATE=1
- # - brew update
- - brew install libimobiledevice
- - brew install ideviceinstaller
- - brew install ios-deploy
- - sudo gem install cocoapods
-
- # install pre-compiled flutter
- - wget --quiet --output-document=flutter.zip https://storage.googleapis.com/flutter_infra/releases/${FLUTTER_CHANNEL}/macos/flutter_macos_v${FLUTTER_VERSION}.zip && unzip -qq flutter.zip > /dev/null && rm flutter.zip
- - export PATH="$PATH":"$HOME/.pub-cache/bin"
- - export PATH=$PWD/flutter/bin:$PWD/flutter/bin/cache/dart-sdk/bin:$PATH
- - flutter doctor -v
- script: travis_retry ./scripts/ci.sh ./bloc_flutter
- cache:
- directories:
- - $HOME/.pub-cache
-
- - <<: *integration-test
- os: linux
- env: built_redux_android
- script: travis_retry ./scripts/ci.sh ./built_redux
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: built_redux_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./built_redux
- - <<: *integration-test
- os: linux
- env: bloc_library_android
- script: travis_retry ./scripts/ci.sh ./bloc_library
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: bloc_library_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./bloc_library
- - <<: *integration-test
- os: linux
- env: firestore_redux_android
- script: travis_retry ./scripts/ci.sh ./firestore_redux
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: firestore_redux_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./firestore_redux
- - <<: *integration-test
- os: linux
- env: frideos_library_android
- script: travis_retry ./scripts/ci.sh ./frideos_library
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: frideos_library_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./frideos_library
- - <<: *integration-test
- os: linux
- env: inherited_widget_android
- script: travis_retry ./scripts/ci.sh ./inherited_widget
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: inherited_widget_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./inherited_widget
- - <<: *integration-test
- os: linux
- env: mvc_android
- script: travis_retry ./scripts/ci.sh ./mvc
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: mvc_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./mvc
- - <<: *integration-test
- os: linux
- env: mvi_flutter_android
- script: travis_retry ./scripts/ci.sh ./mvi_flutter
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: mvi_flutter_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./mvi_flutter
- - <<: *integration-test
- os: linux
- env: mvu_android
- script: travis_retry ./scripts/ci.sh ./mvu
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: mvu_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./mvu
- - <<: *integration-test
- os: linux
- env: redux_android
- script: travis_retry ./scripts/ci.sh ./redux
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: redux_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./redux
- - <<: *integration-test
- os: linux
- env: change_notifier_provider_android
- script: travis_retry ./scripts/ci.sh ./change_notifier_provider
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: change_notifier_provider_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./change_notifier_provider
- - <<: *integration-test
- os: linux
- env: scoped_model_android
- script: travis_retry ./scripts/ci.sh ./scoped_model
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: scoped_model_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./scoped_model
- - <<: *integration-test
- os: linux
- env: simple_bloc_flutter_android
- script: travis_retry ./scripts/ci.sh ./simple_bloc_flutter
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: simple_bloc_flutter_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./simple_bloc_flutter
- - <<: *integration-test
- os: linux
- env: vanilla_android
- script: travis_retry ./scripts/ci.sh ./vanilla
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: vanilla_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./vanilla
- - <<: *integration-test
- os: linux
- env: mobx_android
- script: travis_retry ./scripts/ci.sh ./mobx
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: mobx_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./mobx
- - <<: *integration-test
- os: linux
- env: states_rebuilder_android
- script: travis_retry ./scripts/ci.sh ./states_rebuilder
- - <<: *integration-test
- os: osx
- osx_image: xcode11.3
- env: states_rebuilder_ios
- before_install: *before_install_osx
- script: travis_retry ./scripts/ci.sh ./states_rebuilder
-
- allow_failures:
-# - env:
-# - bloc_flutter_ios
-# - built_redux_android
-# - firestore_redux_android
-# - firestore_redux_ios
-# - mvu_android
-# - mvu_ios
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 791a857f..00000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Changelog
-
-## 0.0.1
-
-- Initial version, created by Stagehand
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 74218fa2..00000000
--- a/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-.PHONY: clean localize all check_env
-
-FILES := $(shell find . -name '*.arb' | xargs)
-
-all: localize
-
-localize: check_env clean
- flutter pub pub run intl_translation:extract_to_arb --output-dir=./ --no-transformer lib/src/localization.dart
- mv intl_messages.arb intl_en.arb
- flutter pub pub run intl_translation:generate_from_arb --no-use-deferred-loading lib/src/localization.dart $(FILES)
- mv messages*.dart lib/src/localizations
-
-clean:
- rm -f *.arb
-
-check_env:
-ifndef FLUTTER_ROOT
- $(error FLUTTER_ROOT is undefined. Please export a FLUTTER_ROOT pointing to the installation of Flutter.)
-endif
diff --git a/README.md b/README.md
index fe2edd8f..646740c2 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
# flutter_architecture_samples
-[](https://travis-ci.org/brianegan/flutter_architecture_samples)
-[](https://cirrus-ci.com/github/brianegan/flutter_architecture_samples)
-[](https://codecov.io/gh/brianegan/flutter_architecture_samples)
+[](https://github.com/brianegan/flutter_architecture_samples/actions/workflows/analyze_test_build.ymll)
+[](https://codecov.io/gh/brianegan/flutter_architecture_samples)
@@ -18,35 +17,30 @@ The Flutter Architecture Samples project demonstrates strategies to help solve
or avoid these common problems. This project implements the same app using
different architectural concepts and tools.
-You can use the samples in this project as a learning reference, or as a
-starting point for creating your own apps. The focus of this project is on
-demonstrating how to structure your code, design your architecture, and the
-eventual impact of adopting these patterns on testing and maintaining your app.
-You can use the techniques demonstrated here in many different ways to build
-apps. Your own particular priorities will impact how you implement the concepts
-in these projects, so you should not consider these samples to be canonical
-examples. To ensure the focus is kept on the aims described above, the app uses
-a simple UI.
+You can use the samples in this project as a learning reference, as a roughly
+apples-to-apples comparison of different approaches, or as a starting point for
+creating your own apps. The focus of this project is on demonstrating how to
+structure your code, design your architecture, and the eventual impact of
+adopting these patterns on testing and maintaining your app. You can use the
+techniques demonstrated here in many different ways to build apps. Your own
+particular priorities will impact how you implement the concepts in these
+projects, so you should not consider these samples to be canonical examples. To
+ensure the focus is kept on the aims described above, the app uses a simple UI.
### Current Samples
-- [Vanilla Lifting State Up Example](vanilla) ([Web Demo](https://fas_vanilla.codemagic.app)) - Uses the tools Flutter provides out of the box to manage app state.
-- [InheritedWidget Example](inherited_widget) ([Web Demo](https://fas_inherited_widget.codemagic.app)) - Uses an InheritedWidget to pass app state down the widget hierarchy.
-- [Change Notifier + Provider Example](change_notifier_provider) ([Web Demo](https://fas_change_notifier_provider.codemagic.app)) - Uses the [ChangeNotifier](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html) class from Flutter with [provider](https://pub.dev/packages/provider) package now recommended by the Flutter team.
-- [BLoC Example](bloc_flutter) ([Web Demo](https://fas_bloc_flutter.codemagic.app)) - An implementation of the BLoC pattern, which uses Sinks for Inputs and Streams for Outputs
-- [Bloc Library Example](bloc_library) ([Web Demo](https://fas_bloc_library.codemagic.app)) - Uses the [bloc](https://pub.dartlang.org/packages/bloc) and [flutter_bloc](https://pub.dartlang.org/packages/flutter_bloc) libraries to manage app state and update Widgets.
-- [MobX Example](mobx) ([Web Demo](https://fas_mobx.codemagic.app)) - Uses the [MobX](https://pub.dev/packages/mobx) library to manage app state and update widgets using `Observables`, `Actions` and `Reactions`.
-- [Redux Example](redux) ([Web Demo](https://fas_redux.codemagic.app)) - Uses the [Redux](https://pub.dartlang.org/packages/redux) library to manage app state and update Widgets
-- ["Simple" BLoC Example](simple_bloc_flutter) ([Web Demo](https://fas_simple_bloc.codemagic.app)) - Similar to the BLoC pattern, but uses Functions for Inputs and Streams for Outputs. Results in far less code compared to standard BLoC.
-- [MVI Example](mvi_flutter) ([Web Demo](https://fas_mvi.codemagic.app)) - Uses the concepts from Cycle.JS and applies them to Flutter.
-- [states_rebuilder Example](states_rebuilder) ([Web Demo](https://fas_states_rebuilder.codemagic.app)) - Uses the [states_rebuilder](https://pub.dev/packages/states_rebuilder) library to manage app state and update Widgets.
-- [built_redux Example](built_redux) - Uses the [built_redux](https://pub.dartlang.org/packages/built_redux) library to enforce immutability and manage app state
-- [scoped_model Example](scoped_model) - Uses the [scoped_model](https://pub.dartlang.org/packages/scoped_model) library to hold app state and notify Widgets of Updates
-- [Firestore Redux Example](firestore_redux) - Uses the [Redux](https://pub.dartlang.org/packages/redux) library to manage app state and update Widgets and
- adds [Cloud_Firestore](https://firebase.google.com/docs/firestore/) as the Todos database.
-- [MVU Example](mvu) - Uses the [dartea](https://pub.dartlang.org/packages/dartea) library to manage app state and update Widgets.
-- [MVC Example](mvc) - Uses the [MVC](https://pub.dartlang.org/packages/mvc_pattern) library to implement the traditional MVC design pattern.
-- [Frideos Example](frideos_library) - Uses the [Frideos](https://pub.dartlang.org/packages/frideos) library to manage app state and update widgets using streams.
+- [Vanilla Lifting State Up Example](vanilla) ([Web Demo](https://fas-vanilla.netlify.app)) - Uses the tools Flutter provides out of the box to manage app state.
+- [InheritedWidget Example](inherited_widget) ([Web Demo](https://fas-inherited-widget.netlify.app)) - Uses an InheritedWidget to pass app state down the widget hierarchy.
+- [Change Notifier + Provider Example](change_notifier_provider) ([Web Demo](https://fas-change-notifier-provider.netlify.app)) - Uses the [ChangeNotifier](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html) class from Flutter with [provider](https://pub.dev/packages/provider) package now recommended by the Flutter team.
+- [Freezed + Provider + Value Notifier](freezed_provider_value_notifier) ([Web Demo](https://fas-freezed-provider-value-notifier.netlify.app)) - Uses the [ValueNotifier](https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html) class from Flutter with [provider](https://pub.dev/packages/provider) package.
+- [BLoC Example](bloc_flutter) ([Web Demo](https://fas-bloc-flutter.netlify.app/)) - An implementation of the original [BLoC pattern](https://www.youtube.com/watch?v=PLHln7wHgPE&list=PLOU2XLYxmsIIJr3vjxggY7yGcGO7i9BK5&index=13) described by Paolo Soares at DartConf 2018, which uses Sinks for Inputs and Streams for Outputs
+- [Bloc Library Example](bloc_library) ([Web Demo](https://fas-bloc-library.netlify.app)) - Uses the [bloc](https://pub.dartlang.org/packages/bloc) and [flutter_bloc](https://pub.dartlang.org/packages/flutter_bloc) libraries to manage app state and update Widgets.
+- [MobX Example](mobx) ([Web Demo](https://fas-mobx.netlify.app)) - Uses the [MobX](https://pub.dev/packages/mobx) library to manage app state and update widgets using `Observables`, `Actions` and `Reactions`.
+- [Redux Example](redux) ([Web Demo](https://fas-redux.netlify.app)) - Uses the [Redux](https://pub.dartlang.org/packages/redux) library to manage app state and update Widgets
+- ["Simple" BLoC Example](simple_bloc_flutter) ([Web Demo](https://fas-simple-bloc.netlify.app/)) - Similar to the BLoC pattern, but uses Functions for Inputs and Streams for Outputs. Results in far less code compared to original BLoC pattern if code sharing with AngularDart apps isn't an important use case for your app.
+- [Signals Example](signals) ([Web Demo](https://fas-signals.netlify.app)) - Uses the [Signals](https://pub.dev/packages/signals) package by [Rody Davis](https://pub.dev/publishers/rodydavis.com/packages).
+- [MVI Example](mvi_flutter) ([Web Demo](https://fas-mvi.netlify.app)) - Uses the concepts from [Cycle.JS](https://cycle.js.org/) and applies them to Flutter.
+- [scoped_model Example](scoped_model) ([Web Demo](https://fas-scoped-model.netlify.app)) - Uses the [scoped_model](https://pub.dartlang.org/packages/scoped_model) library to hold app state and notify Widgets of Updates
### Supporting Code
@@ -60,37 +54,19 @@ window.localStorage for web projects.
- [todos_repository_local_storage](todos_repository_local_storage) - Implements
the todos repository using the file system, window.localStorage, and
SharedPreferences as the data source.
-- [firebase_flutter_repository](firebase_flutter_repository) - Implements
-the todos repository using firestore as the data source.
-- [firebase_rtdb_flutter_repository](firebase_rtdb_flutter_repository) -
-Implements the todos repository using firebase real-time database as the data
-source.
### Running the samples
-#### iOS / Android
-
```
cd
flutter run
```
-#### Web
-
-Make sure you're on Flutter version "Flutter 1.12.13+hotfix.6 • channel beta" or
-newer. Not all samples support web at this time, so please check the sample
-directory for a `lib/main_web.dart` file.
-
-```
-cd
-flutter run -d chrome -t lib/main_web.dart
-```
-
### Why a todo app?
-The app in this project aims to be simple enough that you can understand it
-quickly, but complex enough to showcase difficult design decisions and testing
-scenarios. For more information, see the [app's specification](app_spec.md).
+The app in this project aims to be as simple as possible while still showcasing
+different design decisions and testing scenarios. For more information, see the
+[app's specification](app_spec.md).
### Be excellent to each other
diff --git a/analysis_options.yaml b/analysis_options.yaml
deleted file mode 100644
index 962f9342..00000000
--- a/analysis_options.yaml
+++ /dev/null
@@ -1,56 +0,0 @@
-# Pedantic 1.9.0
-#
-# Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-# for details. All rights reserved. Use of this source code is governed by a
-# BSD-style license that can be found in the LICENSE file.
-#
-# Google internally enforced rules. See README.md for more information,
-# including a list of lints that are intentionally _not_ enforced.
-
-linter:
- rules:
- - always_declare_return_types
- - always_require_non_null_named_parameters
- - annotate_overrides
- - avoid_empty_else
- - avoid_init_to_null
- - avoid_null_checks_in_equality_operators
- - avoid_relative_lib_imports
- - avoid_return_types_on_setters
- - avoid_shadowing_type_parameters
- - avoid_types_as_parameter_names
- - camel_case_extensions
- - curly_braces_in_flow_control_structures
- - empty_catches
- - empty_constructor_bodies
- - library_names
- - library_prefixes
- - no_duplicate_case_values
- - null_closures
- - omit_local_variable_types
- - prefer_adjacent_string_concatenation
- - prefer_collection_literals
- - prefer_conditional_assignment
- - prefer_contains
- - prefer_equal_for_default_values
- - prefer_final_fields
- - prefer_for_elements_to_map_fromIterable
- - prefer_generic_function_type_aliases
- - prefer_if_null_operators
- - prefer_is_empty
- - prefer_is_not_empty
- - prefer_iterable_whereType
- - prefer_single_quotes
- - prefer_spread_collections
- - recursive_getters
- - slash_for_doc_comments
- - type_init_formals
- - unawaited_futures
- - unnecessary_const
- - unnecessary_new
- - unnecessary_null_in_if_null_operators
- - unnecessary_this
- - unrelated_type_equality_checks
- - use_function_type_syntax_for_parameters
- - use_rethrow_when_possible
- - valid_regexps
diff --git a/app_spec.md b/app_spec.md
index 751da2c2..3351d1cc 100644
--- a/app_spec.md
+++ b/app_spec.md
@@ -1,6 +1,6 @@
# Application Specification
-We have created this short spec to help you create awesome and consistent todo apps. Make sure to not only read it but to understand it as well.
+We have created this short spec to help you create consistent todo apps. Consistency is key to form an "apples to apples" comparison between different approaches.
## Reference Application
@@ -17,8 +17,9 @@ All examples must include a README describing the general implementation, any fr
- Format your code with `dartfmt`
- Use the `.analysis_options.yaml` from the vanilla implementation and ensure there are no analysis errors
- Use the Theme and Widgets provided by the base package for the visual look, unless it makes sense to demonstrate an alternative practice.
-- Your app should work on both Android and iOS
+- Your app should work on all platforms
- Your app should contain tests
+- Your app must pass the integration tests
## User Interface
diff --git a/assets/add-todo.png b/assets/add-todo.png
index 606fc63f..2b2d365f 100644
Binary files a/assets/add-todo.png and b/assets/add-todo.png differ
diff --git a/assets/edit-todo.png b/assets/edit-todo.png
index 17579d0a..44851a58 100644
Binary files a/assets/edit-todo.png and b/assets/edit-todo.png differ
diff --git a/assets/favicon-16x16.png b/assets/favicon-16x16.png
index ee713738..d7013256 100644
Binary files a/assets/favicon-16x16.png and b/assets/favicon-16x16.png differ
diff --git a/assets/favicon-32x32.png b/assets/favicon-32x32.png
index cad7bfaf..af89dc70 100644
Binary files a/assets/favicon-32x32.png and b/assets/favicon-32x32.png differ
diff --git a/assets/filter.png b/assets/filter.png
index cca3a364..73eef157 100644
Binary files a/assets/filter.png and b/assets/filter.png differ
diff --git a/assets/mark-all.png b/assets/mark-all.png
index 4a758e54..15ec9dcc 100644
Binary files a/assets/mark-all.png and b/assets/mark-all.png differ
diff --git a/assets/screenshot.png b/assets/screenshot.png
index ec0b619a..03390a8d 100644
Binary files a/assets/screenshot.png and b/assets/screenshot.png differ
diff --git a/assets/stats.png b/assets/stats.png
index d9729597..a1f4e774 100644
Binary files a/assets/stats.png and b/assets/stats.png differ
diff --git a/assets/todo-details.png b/assets/todo-details.png
index 59fd22ba..f5f8445d 100644
Binary files a/assets/todo-details.png and b/assets/todo-details.png differ
diff --git a/assets/todo-list.png b/assets/todo-list.png
index 30f8b5b5..68f55686 100644
Binary files a/assets/todo-list.png and b/assets/todo-list.png differ
diff --git a/bloc_flutter/.metadata b/bloc_flutter/.metadata
index 1b5cec02..05a8ab44 100644
--- a/bloc_flutter/.metadata
+++ b/bloc_flutter/.metadata
@@ -4,7 +4,42 @@
# This file should be version controlled and should not be manually edited.
version:
- revision: 27321ebbad34b0a3fafe99fac037102196d655ff
- channel: stable
+ revision: "05db9689081f091050f01aed79f04dce0c750154"
+ channel: "stable"
project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: android
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: ios
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: linux
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: macos
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: web
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: windows
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/bloc_flutter/analysis_options.yaml b/bloc_flutter/analysis_options.yaml
new file mode 100644
index 00000000..134f2137
--- /dev/null
+++ b/bloc_flutter/analysis_options.yaml
@@ -0,0 +1,34 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+analyzer:
+ language:
+ strict-casts: true
+ strict-inference: true
+ strict-raw-types: true
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at https://dart.dev/lints.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/bloc_flutter/android/.gitignore b/bloc_flutter/android/.gitignore
index bc2100d8..be3943c9 100644
--- a/bloc_flutter/android/.gitignore
+++ b/bloc_flutter/android/.gitignore
@@ -5,3 +5,10 @@ gradle-wrapper.jar
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
+.cxx/
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/to/reference-keystore
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/bloc_flutter/android/app/build.gradle b/bloc_flutter/android/app/build.gradle
deleted file mode 100644
index b0d80781..00000000
--- a/bloc_flutter/android/app/build.gradle
+++ /dev/null
@@ -1,68 +0,0 @@
-def localProperties = new Properties()
-def localPropertiesFile = rootProject.file('local.properties')
-if (localPropertiesFile.exists()) {
- localPropertiesFile.withReader('UTF-8') { reader ->
- localProperties.load(reader)
- }
-}
-
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
-def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
-if (flutterVersionCode == null) {
- flutterVersionCode = '1'
-}
-
-def flutterVersionName = localProperties.getProperty('flutter.versionName')
-if (flutterVersionName == null) {
- flutterVersionName = '1.0'
-}
-
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
-android {
- compileSdkVersion 28
-
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
- }
-
- lintOptions {
- disable 'InvalidPackage'
- }
-
- defaultConfig {
- // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
- applicationId "com.example.bloc_flutter"
- minSdkVersion 16
- targetSdkVersion 28
- versionCode flutterVersionCode.toInteger()
- versionName flutterVersionName
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- multiDexEnabled true
- }
-
- buildTypes {
- release {
- // TODO: Add your own signing config for the release build.
- // Signing with the debug keys for now, so `flutter run --release` works.
- signingConfig signingConfigs.debug
- }
- }
-}
-
-flutter {
- source '../..'
-}
-
-dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test:runner:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
-}
diff --git a/bloc_flutter/android/app/build.gradle.kts b/bloc_flutter/android/app/build.gradle.kts
new file mode 100644
index 00000000..f8e5e923
--- /dev/null
+++ b/bloc_flutter/android/app/build.gradle.kts
@@ -0,0 +1,44 @@
+plugins {
+ id("com.android.application")
+ id("kotlin-android")
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id("dev.flutter.flutter-gradle-plugin")
+}
+
+android {
+ namespace = "com.example.bloc_flutter_sample"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_11.toString()
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId = "com.example.bloc_flutter_sample"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = flutter.minSdkVersion
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ }
+}
+
+flutter {
+ source = "../.."
+}
diff --git a/bloc_flutter/android/app/src/debug/AndroidManifest.xml b/bloc_flutter/android/app/src/debug/AndroidManifest.xml
index 0e7c814b..399f6981 100644
--- a/bloc_flutter/android/app/src/debug/AndroidManifest.xml
+++ b/bloc_flutter/android/app/src/debug/AndroidManifest.xml
@@ -1,6 +1,6 @@
-
-
diff --git a/bloc_flutter/android/app/src/main/AndroidManifest.xml b/bloc_flutter/android/app/src/main/AndroidManifest.xml
index 02fd0f47..9866d0a0 100644
--- a/bloc_flutter/android/app/src/main/AndroidManifest.xml
+++ b/bloc_flutter/android/app/src/main/AndroidManifest.xml
@@ -1,21 +1,25 @@
-
-
+
+
+
@@ -27,4 +31,15 @@
android:name="flutterEmbedding"
android:value="2" />
+
+
+
+
+
+
+
diff --git a/bloc_flutter/android/app/src/main/kotlin/com/example/bloc_flutter/MainActivity.kt b/bloc_flutter/android/app/src/main/kotlin/com/example/bloc_flutter/MainActivity.kt
deleted file mode 100644
index 4a5aea1c..00000000
--- a/bloc_flutter/android/app/src/main/kotlin/com/example/bloc_flutter/MainActivity.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.example.bloc_flutter
-
-import androidx.annotation.NonNull;
-import io.flutter.embedding.android.FlutterActivity
-import io.flutter.embedding.engine.FlutterEngine
-import io.flutter.plugins.GeneratedPluginRegistrant
-
-class MainActivity: FlutterActivity() {
- override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
- GeneratedPluginRegistrant.registerWith(flutterEngine);
- }
-}
diff --git a/bloc_flutter/android/app/src/main/kotlin/com/example/bloc_flutter_sample/MainActivity.kt b/bloc_flutter/android/app/src/main/kotlin/com/example/bloc_flutter_sample/MainActivity.kt
new file mode 100644
index 00000000..c525aee6
--- /dev/null
+++ b/bloc_flutter/android/app/src/main/kotlin/com/example/bloc_flutter_sample/MainActivity.kt
@@ -0,0 +1,5 @@
+package com.example.bloc_flutter_sample
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity : FlutterActivity()
diff --git a/bloc_flutter/android/app/src/main/res/drawable-v21/launch_background.xml b/bloc_flutter/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 00000000..f74085f3
--- /dev/null
+++ b/bloc_flutter/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/bloc_flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/bloc_flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
index a3f285f9..db77bb4b 100644
Binary files a/bloc_flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/bloc_flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/bloc_flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/bloc_flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
index 5e6f3ac6..17987b79 100644
Binary files a/bloc_flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/bloc_flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/bloc_flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/bloc_flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index 144d60be..09d43914 100644
Binary files a/bloc_flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/bloc_flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/bloc_flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/bloc_flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index deafae2d..d5f1c8d3 100644
Binary files a/bloc_flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/bloc_flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/bloc_flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/bloc_flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index d5614ac8..4d6372ee 100644
Binary files a/bloc_flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/bloc_flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/bloc_flutter/android/app/src/main/res/values-night/styles.xml b/bloc_flutter/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 00000000..06952be7
--- /dev/null
+++ b/bloc_flutter/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/bloc_flutter/android/app/src/main/res/values/styles.xml b/bloc_flutter/android/app/src/main/res/values/styles.xml
index 00fa4417..cb1ef880 100644
--- a/bloc_flutter/android/app/src/main/res/values/styles.xml
+++ b/bloc_flutter/android/app/src/main/res/values/styles.xml
@@ -1,8 +1,18 @@
-
+
+
diff --git a/bloc_flutter/android/app/src/profile/AndroidManifest.xml b/bloc_flutter/android/app/src/profile/AndroidManifest.xml
index 0e7c814b..399f6981 100644
--- a/bloc_flutter/android/app/src/profile/AndroidManifest.xml
+++ b/bloc_flutter/android/app/src/profile/AndroidManifest.xml
@@ -1,6 +1,6 @@
-
-
diff --git a/bloc_flutter/android/build.gradle b/bloc_flutter/android/build.gradle
deleted file mode 100644
index 3100ad2d..00000000
--- a/bloc_flutter/android/build.gradle
+++ /dev/null
@@ -1,31 +0,0 @@
-buildscript {
- ext.kotlin_version = '1.3.50'
- repositories {
- google()
- jcenter()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:3.5.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
-}
-
-allprojects {
- repositories {
- google()
- jcenter()
- }
-}
-
-rootProject.buildDir = '../build'
-subprojects {
- project.buildDir = "${rootProject.buildDir}/${project.name}"
-}
-subprojects {
- project.evaluationDependsOn(':app')
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
diff --git a/bloc_flutter/android/build.gradle.kts b/bloc_flutter/android/build.gradle.kts
new file mode 100644
index 00000000..dbee657b
--- /dev/null
+++ b/bloc_flutter/android/build.gradle.kts
@@ -0,0 +1,24 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+val newBuildDir: Directory =
+ rootProject.layout.buildDirectory
+ .dir("../../build")
+ .get()
+rootProject.layout.buildDirectory.value(newBuildDir)
+
+subprojects {
+ val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
+ project.layout.buildDirectory.value(newSubprojectBuildDir)
+}
+subprojects {
+ project.evaluationDependsOn(":app")
+}
+
+tasks.register("clean") {
+ delete(rootProject.layout.buildDirectory)
+}
diff --git a/bloc_flutter/android/gradle.properties b/bloc_flutter/android/gradle.properties
index 38c8d454..f018a618 100644
--- a/bloc_flutter/android/gradle.properties
+++ b/bloc_flutter/android/gradle.properties
@@ -1,4 +1,3 @@
-org.gradle.jvmargs=-Xmx1536M
-android.enableR8=true
+org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
diff --git a/bloc_flutter/android/gradle/wrapper/gradle-wrapper.properties b/bloc_flutter/android/gradle/wrapper/gradle-wrapper.properties
index 296b146b..ac3b4792 100644
--- a/bloc_flutter/android/gradle/wrapper/gradle-wrapper.properties
+++ b/bloc_flutter/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
diff --git a/bloc_flutter/android/settings.gradle b/bloc_flutter/android/settings.gradle
deleted file mode 100644
index 5a2f14fb..00000000
--- a/bloc_flutter/android/settings.gradle
+++ /dev/null
@@ -1,15 +0,0 @@
-include ':app'
-
-def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
-
-def plugins = new Properties()
-def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
-if (pluginsFile.exists()) {
- pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
-}
-
-plugins.each { name, path ->
- def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
- include ":$name"
- project(":$name").projectDir = pluginDirectory
-}
diff --git a/bloc_flutter/android/settings.gradle.kts b/bloc_flutter/android/settings.gradle.kts
new file mode 100644
index 00000000..fb605bc8
--- /dev/null
+++ b/bloc_flutter/android/settings.gradle.kts
@@ -0,0 +1,26 @@
+pluginManagement {
+ val flutterSdkPath =
+ run {
+ val properties = java.util.Properties()
+ file("local.properties").inputStream().use { properties.load(it) }
+ val flutterSdkPath = properties.getProperty("flutter.sdk")
+ require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
+ flutterSdkPath
+ }
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id("dev.flutter.flutter-plugin-loader") version "1.0.0"
+ id("com.android.application") version "8.9.1" apply false
+ id("org.jetbrains.kotlin.android") version "2.1.0" apply false
+}
+
+include(":app")
diff --git a/bloc_flutter/integration_test/app_test.dart b/bloc_flutter/integration_test/app_test.dart
new file mode 100644
index 00000000..26407e76
--- /dev/null
+++ b/bloc_flutter/integration_test/app_test.dart
@@ -0,0 +1,26 @@
+import 'package:bloc_flutter_sample/anonymous_user_repository.dart';
+import 'package:bloc_flutter_sample/app.dart';
+import 'package:blocs/blocs.dart';
+import 'package:integration_tests/integration_tests.dart' as integration_tests;
+import 'package:shared_preferences/shared_preferences.dart';
+import 'package:todos_repository_local_storage/todos_repository_local_storage.dart';
+
+void main() {
+ integration_tests.run(
+ appBuilder: () async {
+ return BlocApp(
+ todosInteractor: TodosInteractor(
+ ReactiveLocalStorageRepository(
+ repository: LocalStorageRepository(
+ localStorage: KeyValueStorage(
+ 'bloc_flutter_test_${DateTime.now().toIso8601String()}',
+ await SharedPreferences.getInstance(),
+ ),
+ ),
+ ),
+ ),
+ userRepository: AnonymousUserRepository(),
+ );
+ },
+ );
+}
diff --git a/bloc_flutter/ios/.gitignore b/bloc_flutter/ios/.gitignore
index e96ef602..7a7f9873 100644
--- a/bloc_flutter/ios/.gitignore
+++ b/bloc_flutter/ios/.gitignore
@@ -1,3 +1,4 @@
+**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
@@ -18,6 +19,7 @@ Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
+Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
diff --git a/bloc_flutter/ios/Flutter/AppFrameworkInfo.plist b/bloc_flutter/ios/Flutter/AppFrameworkInfo.plist
index 6b4c0f78..1dc6cf76 100644
--- a/bloc_flutter/ios/Flutter/AppFrameworkInfo.plist
+++ b/bloc_flutter/ios/Flutter/AppFrameworkInfo.plist
@@ -3,7 +3,7 @@
CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
+ en
CFBundleExecutable
App
CFBundleIdentifier
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 8.0
+ 13.0
diff --git a/bloc_flutter/ios/Flutter/Debug.xcconfig b/bloc_flutter/ios/Flutter/Debug.xcconfig
index e8efba11..ec97fc6f 100644
--- a/bloc_flutter/ios/Flutter/Debug.xcconfig
+++ b/bloc_flutter/ios/Flutter/Debug.xcconfig
@@ -1,2 +1,2 @@
-#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
diff --git a/bloc_flutter/ios/Flutter/Release.xcconfig b/bloc_flutter/ios/Flutter/Release.xcconfig
index 399e9340..c4855bfe 100644
--- a/bloc_flutter/ios/Flutter/Release.xcconfig
+++ b/bloc_flutter/ios/Flutter/Release.xcconfig
@@ -1,2 +1,2 @@
-#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
diff --git a/bloc_flutter/ios/Podfile b/bloc_flutter/ios/Podfile
index b30a428b..620e46eb 100644
--- a/bloc_flutter/ios/Podfile
+++ b/bloc_flutter/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-# platform :ios, '9.0'
+# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -10,81 +10,34 @@ project 'Runner', {
'Release' => :release,
}
-def parse_KV_file(file, separator='=')
- file_abs_path = File.expand_path(file)
- if !File.exists? file_abs_path
- return [];
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
- generated_key_values = {}
- skip_line_start_symbols = ["#", "/"]
- File.foreach(file_abs_path) do |line|
- next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
- plugin = line.split(pattern=separator)
- if plugin.length == 2
- podname = plugin[0].strip()
- path = plugin[1].strip()
- podpath = File.expand_path("#{path}", file_abs_path)
- generated_key_values[podname] = podpath
- else
- puts "Invalid plugin specification: #{line}"
- end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
end
- generated_key_values
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
-target 'Runner' do
- use_frameworks!
- use_modular_headers!
-
- # Flutter Pod
-
- copied_flutter_dir = File.join(__dir__, 'Flutter')
- copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
- copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
- unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
- # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
- # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
- # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
-
- generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
- unless File.exist?(generated_xcode_build_settings_path)
- raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
- end
- generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
- cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
- unless File.exist?(copied_framework_path)
- FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
- end
- unless File.exist?(copied_podspec_path)
- FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
- end
- end
-
- # Keep pod path relative so it can be checked into Podfile.lock.
- pod 'Flutter', :path => 'Flutter'
+flutter_ios_podfile_setup
- # Plugin Pods
+target 'Runner' do
+ use_frameworks!
- # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
- # referring to absolute paths on developers' machines.
- system('rm -rf .symlinks')
- system('mkdir -p .symlinks/plugins')
- plugin_pods = parse_KV_file('../.flutter-plugins')
- plugin_pods.each do |name, path|
- symlink = File.join('.symlinks', 'plugins', name)
- File.symlink(path, symlink)
- pod name, :path => File.join(symlink, 'ios')
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
end
end
-# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
-install! 'cocoapods', :disable_input_output_paths => true
-
post_install do |installer|
installer.pods_project.targets.each do |target|
- target.build_configurations.each do |config|
- config.build_settings['ENABLE_BITCODE'] = 'NO'
- end
+ flutter_additional_ios_build_settings(target)
end
end
diff --git a/bloc_flutter/ios/Runner.xcodeproj/project.pbxproj b/bloc_flutter/ios/Runner.xcodeproj/project.pbxproj
index a861f735..92c13a07 100644
--- a/bloc_flutter/ios/Runner.xcodeproj/project.pbxproj
+++ b/bloc_flutter/ios/Runner.xcodeproj/project.pbxproj
@@ -3,24 +3,29 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
- 2366E5FF7225C36570C36ADF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B18CA89E2996A6B263F66545 /* Pods_Runner.framework */; };
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
- 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
- 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
- 4EEE3E8F23C4927D0065A5A2 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4EEE3E8E23C4927D0065A5A2 /* GoogleService-Info.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
- 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
- 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+/* End PBXContainerItemProxy section */
+
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
@@ -28,8 +33,6 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
- 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
- 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@@ -39,24 +42,19 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
- 3219E38D9738F4AC7121FB7B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
- 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
- 4EEE3E8E23C4927D0065A5A2 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
- 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- B18CA89E2996A6B263F66545 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- DD8741825F163AC04489B25F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
- EB174B8697ECB290E7346D6E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -64,40 +62,24 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
- 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
- 2366E5FF7225C36570C36ADF /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
- 4DEAD570AC93508D92C128F4 /* Pods */ = {
- isa = PBXGroup;
- children = (
- EB174B8697ECB290E7346D6E /* Pods-Runner.debug.xcconfig */,
- 3219E38D9738F4AC7121FB7B /* Pods-Runner.release.xcconfig */,
- DD8741825F163AC04489B25F /* Pods-Runner.profile.xcconfig */,
- );
- name = Pods;
- path = Pods;
- sourceTree = "";
- };
- 65314717153277BBBD59EF57 /* Frameworks */ = {
+ 331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
- B18CA89E2996A6B263F66545 /* Pods_Runner.framework */,
+ 331C807B294A618700263BE5 /* RunnerTests.swift */,
);
- name = Frameworks;
+ path = RunnerTests;
sourceTree = "";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
- 3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
- 9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
@@ -108,12 +90,10 @@
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
- 4EEE3E8E23C4927D0065A5A2 /* GoogleService-Info.plist */,
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
- 4DEAD570AC93508D92C128F4 /* Pods */,
- 65314717153277BBBD59EF57 /* Frameworks */,
+ 331C8082294A63A400263BE5 /* RunnerTests */,
);
sourceTree = "";
};
@@ -121,6 +101,7 @@
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "";
@@ -132,7 +113,6 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
- 97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
@@ -141,28 +121,36 @@
path = Runner;
sourceTree = "";
};
- 97C146F11CF9000F007C117D /* Supporting Files */ = {
- isa = PBXGroup;
- children = (
- );
- name = "Supporting Files";
- sourceTree = "";
- };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ 331C8080294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ 331C807D294A63A400263BE5 /* Sources */,
+ 331C807F294A63A400263BE5 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- B4149F1FCA3B6359C6408BDA /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- 7D98778AC99E43569AB862F5 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -179,9 +167,14 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1020;
- ORGANIZATIONNAME = "The Chromium Authors";
+ BuildIndependentTargetsInParallel = YES;
+ LastUpgradeCheck = 1510;
+ ORGANIZATIONNAME = "";
TargetAttributes = {
+ 331C8080294A63A400263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
@@ -189,7 +182,7 @@
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
- compatibilityVersion = "Xcode 3.2";
+ compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -202,18 +195,25 @@
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
+ 331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 331C807F294A63A400263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
- 4EEE3E8F23C4927D0065A5A2 /* GoogleService-Info.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
@@ -224,35 +224,23 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
- };
- 7D98778AC99E43569AB862F5 /* [CP] Embed Pods Frameworks */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "[CP] Embed Pods Frameworks";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
- showEnvVarsInLog = 0;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@@ -265,31 +253,17 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
- B4149F1FCA3B6359C6408BDA /* [CP] Check Pods Manifest.lock */ = {
- isa = PBXShellScriptBuildPhase;
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 331C807D294A63A400263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- );
- inputFileListPaths = (
- );
- inputPaths = (
- "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
- "${PODS_ROOT}/Manifest.lock",
- );
- name = "[CP] Check Pods Manifest.lock";
- outputFileListPaths = (
- );
- outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
- showEnvVarsInLog = 0;
};
-/* End PBXShellScriptBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -301,6 +275,14 @@
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
@@ -323,9 +305,9 @@
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -355,6 +337,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -363,7 +346,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -379,18 +362,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 8W92H589V9;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
+ LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
- "$(PROJECT_DIR)/Flutter",
+ "@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutter;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutterSample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -398,11 +377,58 @@
};
name = Profile;
};
+ 331C8088294A63A400263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutterSample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Debug;
+ };
+ 331C8089294A63A400263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutterSample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Release;
+ };
+ 331C808A294A63A400263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutterSample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Profile;
+ };
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -432,6 +458,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -446,7 +473,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -456,9 +483,9 @@
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -488,6 +515,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -496,11 +524,12 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
- SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@@ -513,18 +542,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 8W92H589V9;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
+ LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
- "$(PROJECT_DIR)/Flutter",
+ "@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutter;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutterSample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -540,18 +565,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 8W92H589V9;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
+ LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
- "$(PROJECT_DIR)/Flutter",
+ "@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutter;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutterSample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -562,6 +583,16 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C8088294A63A400263BE5 /* Debug */,
+ 331C8089294A63A400263BE5 /* Release */,
+ 331C808A294A63A400263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/bloc_flutter/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/bloc_flutter/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 1d526a16..919434a6 100644
--- a/bloc_flutter/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/bloc_flutter/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -2,6 +2,6 @@
+ location = "self:">
diff --git a/bloc_flutter/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/bloc_flutter/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/bloc_flutter/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/bloc_flutter/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/bloc_flutter/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/bloc_flutter/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/bloc_flutter/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/bloc_flutter/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index a28140cf..e3773d42 100644
--- a/bloc_flutter/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/bloc_flutter/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
-
-
-
-
+
+
+
+
+
+
@@ -61,8 +73,6 @@
ReferencedContainer = "container:Runner.xcodeproj">
-
-
-
-
diff --git a/bloc_flutter/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/bloc_flutter/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/bloc_flutter/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/bloc_flutter/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/bloc_flutter/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/bloc_flutter/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/bloc_flutter/ios/Runner/AppDelegate.swift b/bloc_flutter/ios/Runner/AppDelegate.swift
index 70693e4a..62666446 100644
--- a/bloc_flutter/ios/Runner/AppDelegate.swift
+++ b/bloc_flutter/ios/Runner/AppDelegate.swift
@@ -1,7 +1,7 @@
-import UIKit
import Flutter
+import UIKit
-@UIApplicationMain
+@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
index d22f10b2..d36b1fab 100644
--- a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -107,6 +107,12 @@
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
}
],
"info" : {
diff --git a/states_rebuilder/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
similarity index 100%
rename from states_rebuilder/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
rename to bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
index 980e5ad6..7353c41e 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
index fd870289..797d452e 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
index 75e84cd1..6ed2d933 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
index 03ab8a84..4cd7b009 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
index a03431cb..fe730945 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
index f47613ee..321773cd 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
index 7f2230a9..797d452e 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
index 42315c6d..502f463a 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
index f9882cc0..0ec30343 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png
deleted file mode 100644
index 8c552e23..00000000
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png and /dev/null differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
index 45537513..0ec30343 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
index 6360ea17..e9f5fea2 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
index 152d5e12..84ac32ae 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
index 310b0b8f..8953cba0 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png
deleted file mode 100644
index 40ac4ea7..00000000
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png and /dev/null differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@1x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@1x.png
deleted file mode 100644
index dfc408df..00000000
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@1x.png and /dev/null differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index 092b7bfe..0467bf12 100644
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@3x.png b/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@3x.png
deleted file mode 100644
index 521c3e2a..00000000
Binary files a/bloc_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@3x.png and /dev/null differ
diff --git a/bloc_flutter/ios/Runner/GoogleService-Info.plist b/bloc_flutter/ios/Runner/GoogleService-Info.plist
deleted file mode 100644
index c8cc9bf9..00000000
--- a/bloc_flutter/ios/Runner/GoogleService-Info.plist
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
- AD_UNIT_ID_FOR_BANNER_TEST
- ca-app-pub-9999999999999999/9999999999
- AD_UNIT_ID_FOR_INTERSTITIAL_TEST
- ca-app-pub-9999999999999999/9999999999
- CLIENT_ID
- xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com
- REVERSED_CLIENT_ID
- com.googleusercontent.apps.xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- API_KEY
- xxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx
- GCM_SENDER_ID
- 999999999999
- PLIST_VERSION
- 1
- BUNDLE_ID
- com.fluttersamples.bloc
- PROJECT_ID
- xxxxxxxxxxxxx-99999
- STORAGE_BUCKET
- xxxxxxxxxxxxx-99999.appspot.com
- IS_ADS_ENABLED
-
- IS_ANALYTICS_ENABLED
-
- IS_APPINVITE_ENABLED
-
- IS_GCM_ENABLED
-
- IS_SIGNIN_ENABLED
-
- GOOGLE_APP_ID
- 9:999999999999:ios:xxxxxxxxxxxxxxxx
- DATABASE_URL
- https://xxxxxxxxxxxxx-99999.firebaseio.com
-
-
\ No newline at end of file
diff --git a/bloc_flutter/ios/Runner/Info.plist b/bloc_flutter/ios/Runner/Info.plist
index dfc13dd4..b34b0a4a 100644
--- a/bloc_flutter/ios/Runner/Info.plist
+++ b/bloc_flutter/ios/Runner/Info.plist
@@ -4,6 +4,8 @@
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Bloc Flutter Sample
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
@@ -11,7 +13,7 @@
CFBundleInfoDictionaryVersion
6.0
CFBundleName
- bloc_flutter
+ bloc_flutter_sample
CFBundlePackageType
APPL
CFBundleShortVersionString
@@ -39,7 +41,9 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- UIViewControllerBasedStatusBarAppearance
-
+ CADisableMinimumFrameDurationOnPhone
+
+ UIApplicationSupportsIndirectInputEvents
+
diff --git a/bloc_flutter/ios/Runner/Runner-Bridging-Header.h b/bloc_flutter/ios/Runner/Runner-Bridging-Header.h
index 7335fdf9..308a2a56 100644
--- a/bloc_flutter/ios/Runner/Runner-Bridging-Header.h
+++ b/bloc_flutter/ios/Runner/Runner-Bridging-Header.h
@@ -1 +1 @@
-#import "GeneratedPluginRegistrant.h"
\ No newline at end of file
+#import "GeneratedPluginRegistrant.h"
diff --git a/bloc_flutter/ios/RunnerTests/RunnerTests.swift b/bloc_flutter/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 00000000..86a7c3b1
--- /dev/null
+++ b/bloc_flutter/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import Flutter
+import UIKit
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/bloc_flutter/lib/anonymous_user_repository.dart b/bloc_flutter/lib/anonymous_user_repository.dart
new file mode 100644
index 00000000..efd978f5
--- /dev/null
+++ b/bloc_flutter/lib/anonymous_user_repository.dart
@@ -0,0 +1,7 @@
+import 'package:todos_repository_core/todos_repository_core.dart';
+
+class AnonymousUserRepository implements UserRepository {
+ @override
+ Future login() async =>
+ UserEntity(id: 'anonymous', displayName: '', photoUrl: '');
+}
diff --git a/bloc_flutter/lib/app.dart b/bloc_flutter/lib/app.dart
index 44f322e9..fc998dc8 100644
--- a/bloc_flutter/lib/app.dart
+++ b/bloc_flutter/lib/app.dart
@@ -1,7 +1,3 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:bloc_flutter_sample/dependency_injection.dart';
import 'package:bloc_flutter_sample/localization.dart';
import 'package:bloc_flutter_sample/screens/add_edit_screen.dart';
@@ -9,7 +5,6 @@ import 'package:bloc_flutter_sample/screens/home_screen.dart';
import 'package:bloc_flutter_sample/widgets/todos_bloc_provider.dart';
import 'package:blocs/blocs.dart';
import 'package:flutter/material.dart';
-import 'package:meta/meta.dart';
import 'package:todos_app_core/todos_app_core.dart';
import 'package:todos_repository_core/todos_repository_core.dart';
@@ -18,10 +13,10 @@ class BlocApp extends StatelessWidget {
final UserRepository userRepository;
const BlocApp({
- Key key,
- @required this.todosInteractor,
- @required this.userRepository,
- }) : super(key: key);
+ super.key,
+ required this.todosInteractor,
+ required this.userRepository,
+ });
@override
Widget build(BuildContext context) {
@@ -32,15 +27,16 @@ class BlocApp extends StatelessWidget {
bloc: TodosListBloc(todosInteractor),
child: MaterialApp(
onGenerateTitle: (context) => BlocLocalizations.of(context).appTitle,
- theme: ArchSampleTheme.theme,
+ theme: ArchSampleTheme.lightTheme,
+ darkTheme: ArchSampleTheme.darkTheme,
localizationsDelegates: [
ArchSampleLocalizationsDelegate(),
- InheritedWidgetLocalizationsDelegate(),
+ BlocLocalizationsDelegate(),
],
routes: {
ArchSampleRoutes.home: (context) {
return HomeScreen(
- repository: Injector.of(context).userRepository,
+ userRepository: Injector.of(context).userRepository,
);
},
ArchSampleRoutes.addTodo: (context) {
diff --git a/bloc_flutter/lib/dependency_injection.dart b/bloc_flutter/lib/dependency_injection.dart
index 2bfdc6b1..0dfc12a7 100644
--- a/bloc_flutter/lib/dependency_injection.dart
+++ b/bloc_flutter/lib/dependency_injection.dart
@@ -1,29 +1,20 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
-// A poor man's DI. This should be replaced by a proper solution once they
-// are more stable.
-library dependency_injector;
-
import 'package:blocs/blocs.dart';
import 'package:flutter/widgets.dart';
-import 'package:meta/meta.dart';
import 'package:todos_repository_core/todos_repository_core.dart';
class Injector extends InheritedWidget {
final TodosInteractor todosInteractor;
final UserRepository userRepository;
- Injector({
- Key key,
- @required this.todosInteractor,
- @required this.userRepository,
- @required Widget child,
- }) : super(key: key, child: child);
+ const Injector({
+ super.key,
+ required this.todosInteractor,
+ required this.userRepository,
+ required super.child,
+ });
static Injector of(BuildContext context) =>
- context.dependOnInheritedWidgetOfExactType();
+ context.dependOnInheritedWidgetOfExactType()!;
@override
bool updateShouldNotify(Injector oldWidget) =>
diff --git a/bloc_flutter/lib/localization.dart b/bloc_flutter/lib/localization.dart
index 78698325..bf8ca9c2 100644
--- a/bloc_flutter/lib/localization.dart
+++ b/bloc_flutter/lib/localization.dart
@@ -1,27 +1,23 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'dart:async';
import 'package:flutter/material.dart';
class BlocLocalizations {
static BlocLocalizations of(BuildContext context) {
- return Localizations.of(context, BlocLocalizations);
+ return Localizations.of(context, BlocLocalizations)!;
}
String get appTitle => 'Bloc Example';
}
-class InheritedWidgetLocalizationsDelegate
+class BlocLocalizationsDelegate
extends LocalizationsDelegate {
@override
Future load(Locale locale) =>
Future(() => BlocLocalizations());
@override
- bool shouldReload(InheritedWidgetLocalizationsDelegate old) => false;
+ bool shouldReload(BlocLocalizationsDelegate old) => false;
@override
bool isSupported(Locale locale) =>
diff --git a/bloc_flutter/lib/main.dart b/bloc_flutter/lib/main.dart
index 29c83666..5d4d3729 100644
--- a/bloc_flutter/lib/main.dart
+++ b/bloc_flutter/lib/main.dart
@@ -1,38 +1,28 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'dart:async';
+import 'package:bloc_flutter_sample/anonymous_user_repository.dart';
import 'package:bloc_flutter_sample/app.dart';
import 'package:blocs/blocs.dart';
import 'package:flutter/widgets.dart';
-import 'package:key_value_store_flutter/key_value_store_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
-import 'package:todos_repository_core/todos_repository_core.dart';
import 'package:todos_repository_local_storage/todos_repository_local_storage.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
- runApp(BlocApp(
- todosInteractor: TodosInteractor(
- ReactiveLocalStorageRepository(
- repository: LocalStorageRepository(
- localStorage: KeyValueStorage(
- 'bloc_todos',
- FlutterKeyValueStore(await SharedPreferences.getInstance()),
+ runApp(
+ BlocApp(
+ todosInteractor: TodosInteractor(
+ ReactiveLocalStorageRepository(
+ repository: LocalStorageRepository(
+ localStorage: KeyValueStorage(
+ 'bloc_todos',
+ await SharedPreferences.getInstance(),
+ ),
),
),
),
+ userRepository: AnonymousUserRepository(),
),
- userRepository: AnonymousUserRepository(),
- ));
-}
-
-class AnonymousUserRepository implements UserRepository {
- @override
- Future login() {
- return Future.value(UserEntity(id: 'anonymous'));
- }
+ );
}
diff --git a/bloc_flutter/lib/main_firebase.dart b/bloc_flutter/lib/main_firebase.dart
deleted file mode 100644
index a78bef69..00000000
--- a/bloc_flutter/lib/main_firebase.dart
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
-import 'package:bloc_flutter_sample/app.dart';
-import 'package:blocs/blocs.dart';
-import 'package:cloud_firestore/cloud_firestore.dart';
-import 'package:firebase_auth/firebase_auth.dart';
-import 'package:firebase_flutter_repository/reactive_todos_repository.dart';
-import 'package:firebase_flutter_repository/user_repository.dart';
-import 'package:flutter/widgets.dart';
-
-void main() {
- WidgetsFlutterBinding.ensureInitialized();
-
- runApp(BlocApp(
- todosInteractor: TodosInteractor(
- FirestoreReactiveTodosRepository(Firestore.instance),
- ),
- userRepository: FirebaseUserRepository(FirebaseAuth.instance),
- ));
-}
diff --git a/bloc_flutter/lib/main_web.dart b/bloc_flutter/lib/main_web.dart
deleted file mode 100644
index fb868ff1..00000000
--- a/bloc_flutter/lib/main_web.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
-import 'dart:async';
-import 'dart:html';
-
-import 'package:bloc_flutter_sample/app.dart';
-import 'package:blocs/blocs.dart';
-import 'package:flutter/widgets.dart';
-import 'package:key_value_store_web/key_value_store_web.dart';
-import 'package:todos_repository_core/todos_repository_core.dart';
-import 'package:todos_repository_local_storage/todos_repository_local_storage.dart';
-
-Future main() async {
- WidgetsFlutterBinding.ensureInitialized();
-
- runApp(BlocApp(
- todosInteractor: TodosInteractor(
- ReactiveLocalStorageRepository(
- repository: LocalStorageRepository(
- localStorage: KeyValueStorage(
- 'bloc_todos',
- WebKeyValueStore(window.localStorage),
- ),
- ),
- ),
- ),
- userRepository: AnonymousUserRepository(),
- ));
-}
-
-class AnonymousUserRepository implements UserRepository {
- @override
- Future login() {
- return Future.value(UserEntity(id: 'anonymous'));
- }
-}
diff --git a/bloc_flutter/lib/screens/add_edit_screen.dart b/bloc_flutter/lib/screens/add_edit_screen.dart
index 611399e4..6bbe35b1 100644
--- a/bloc_flutter/lib/screens/add_edit_screen.dart
+++ b/bloc_flutter/lib/screens/add_edit_screen.dart
@@ -1,37 +1,33 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
-import 'dart:async';
-
import 'package:blocs/blocs.dart';
import 'package:flutter/material.dart';
import 'package:todos_app_core/todos_app_core.dart';
class AddEditScreen extends StatefulWidget {
- final Todo todo;
- final Function(Todo) addTodo;
- final Function(Todo) updateTodo;
+ final Todo? todo;
+ final void Function(Todo)? addTodo;
+ final void Function(Todo)? updateTodo;
- AddEditScreen({
- Key key,
+ const AddEditScreen({
+ super.key = ArchSampleKeys.addTodoScreen,
this.todo,
this.addTodo,
this.updateTodo,
- }) : super(key: key ?? ArchSampleKeys.addTodoScreen);
+ });
@override
- _AddEditScreenState createState() => _AddEditScreenState();
+ AddEditScreenState createState() => AddEditScreenState();
}
-class _AddEditScreenState extends State {
+class AddEditScreenState extends State {
static final GlobalKey formKey = GlobalKey();
- String _task;
- String _note;
+ late String _task;
+ late String _note;
@override
Widget build(BuildContext context) {
+ final isEditing = widget.todo != null;
+
return Scaffold(
appBar: AppBar(
title: Text(
@@ -44,58 +40,56 @@ class _AddEditScreenState extends State {
padding: EdgeInsets.all(16.0),
child: Form(
key: formKey,
- autovalidate: false,
- onWillPop: () {
- return Future(() => true);
- },
+ autovalidateMode: AutovalidateMode.always,
+ canPop: true,
child: ListView(
children: [
TextFormField(
- initialValue: widget.todo != null ? widget.todo.task : '',
+ initialValue: isEditing ? widget.todo!.task : '',
key: ArchSampleKeys.taskField,
autofocus: isEditing ? false : true,
- style: Theme.of(context).textTheme.headline,
+ style: Theme.of(context).textTheme.headlineSmall,
decoration: InputDecoration(
hintText: ArchSampleLocalizations.of(context).newTodoHint,
),
- validator: (val) => val.trim().isEmpty
+ validator: (val) => val != null && val.trim().isEmpty
? ArchSampleLocalizations.of(context).emptyTodoError
: null,
- onSaved: (value) => _task = value,
+ onSaved: (value) => _task = value ?? '',
),
TextFormField(
- initialValue: widget.todo != null ? widget.todo.note : '',
+ initialValue: isEditing ? widget.todo!.note : '',
key: ArchSampleKeys.noteField,
maxLines: 10,
- style: Theme.of(context).textTheme.subhead,
+ style: Theme.of(context).textTheme.titleMedium,
decoration: InputDecoration(
hintText: ArchSampleLocalizations.of(context).notesHint,
),
- onSaved: (value) => _note = value,
- )
+ onSaved: (value) => _note = value ?? '',
+ ),
],
),
),
),
floatingActionButton: FloatingActionButton(
- key:
- isEditing ? ArchSampleKeys.saveTodoFab : ArchSampleKeys.saveNewTodo,
+ key: isEditing
+ ? ArchSampleKeys.saveTodoFab
+ : ArchSampleKeys.saveNewTodo,
tooltip: isEditing
? ArchSampleLocalizations.of(context).saveChanges
: ArchSampleLocalizations.of(context).addTodo,
child: Icon(isEditing ? Icons.check : Icons.add),
onPressed: () {
final form = formKey.currentState;
- if (form.validate()) {
+ if (form!.validate()) {
form.save();
if (isEditing) {
- widget.updateTodo(widget.todo.copyWith(task: _task, note: _note));
+ widget.updateTodo!(
+ widget.todo!.copyWith(task: _task, note: _note),
+ );
} else {
- widget.addTodo(Todo(
- _task,
- note: _note,
- ));
+ widget.addTodo!(Todo(_task, note: _note));
}
Navigator.pop(context);
@@ -104,6 +98,4 @@ class _AddEditScreenState extends State {
),
);
}
-
- bool get isEditing => widget.todo != null;
}
diff --git a/bloc_flutter/lib/screens/detail_screen.dart b/bloc_flutter/lib/screens/detail_screen.dart
index d11cd801..b410c129 100644
--- a/bloc_flutter/lib/screens/detail_screen.dart
+++ b/bloc_flutter/lib/screens/detail_screen.dart
@@ -1,11 +1,6 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:bloc_flutter_sample/screens/add_edit_screen.dart';
import 'package:bloc_flutter_sample/widgets/loading.dart';
import 'package:blocs/blocs.dart';
-import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:todos_app_core/todos_app_core.dart';
@@ -13,10 +8,11 @@ class DetailScreen extends StatefulWidget {
final String todoId;
final TodoBloc Function() initBloc;
- DetailScreen({
- @required this.todoId,
- @required this.initBloc,
- }) : super(key: ArchSampleKeys.todoDetailsScreen);
+ const DetailScreen({
+ super.key = ArchSampleKeys.todoDetailsScreen,
+ required this.todoId,
+ required this.initBloc,
+ });
@override
DetailScreenState createState() {
@@ -25,7 +21,7 @@ class DetailScreen extends StatefulWidget {
}
class DetailScreenState extends State {
- TodoBloc todoBloc;
+ late TodoBloc todoBloc;
@override
void initState() {
@@ -42,11 +38,11 @@ class DetailScreenState extends State {
@override
Widget build(BuildContext context) {
return StreamBuilder(
- stream: todoBloc.todo(widget.todoId).where((todo) => todo != null),
+ stream: todoBloc.todo(widget.todoId),
builder: (context, snapshot) {
if (!snapshot.hasData) return LoadingSpinner();
- final todo = snapshot.data;
+ final todo = snapshot.data!;
return Scaffold(
appBar: AppBar(
@@ -60,7 +56,7 @@ class DetailScreenState extends State {
todoBloc.deleteTodo.add(todo.id);
Navigator.pop(context, todo);
},
- )
+ ),
],
),
body: Padding(
@@ -76,8 +72,9 @@ class DetailScreenState extends State {
value: todo.complete,
key: ArchSampleKeys.detailsTodoItemCheckbox,
onChanged: (complete) {
- todoBloc.updateTodo
- .add(todo.copyWith(complete: !todo.complete));
+ todoBloc.updateTodo.add(
+ todo.copyWith(complete: !todo.complete),
+ );
},
),
),
@@ -86,21 +83,18 @@ class DetailScreenState extends State {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
- padding: EdgeInsets.only(
- top: 8.0,
- bottom: 16.0,
- ),
+ padding: EdgeInsets.only(top: 8.0, bottom: 16.0),
child: Text(
todo.task,
key: ArchSampleKeys.detailsTodoItemTask,
- style: Theme.of(context).textTheme.headline,
+ style: Theme.of(context).textTheme.headlineSmall,
),
),
Text(
todo.note,
key: ArchSampleKeys.detailsTodoItemNote,
- style: Theme.of(context).textTheme.subhead,
- )
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
],
),
),
@@ -111,11 +105,10 @@ class DetailScreenState extends State {
),
floatingActionButton: FloatingActionButton(
tooltip: ArchSampleLocalizations.of(context).editTodo,
- child: Icon(Icons.edit),
key: ArchSampleKeys.editTodoFab,
onPressed: () {
Navigator.of(context).push(
- MaterialPageRoute(
+ MaterialPageRoute(
builder: (context) {
return AddEditScreen(
todo: todo,
@@ -126,6 +119,7 @@ class DetailScreenState extends State {
),
);
},
+ child: Icon(Icons.edit),
),
);
},
diff --git a/bloc_flutter/lib/screens/home_screen.dart b/bloc_flutter/lib/screens/home_screen.dart
index 9429c3a9..f50e1e19 100644
--- a/bloc_flutter/lib/screens/home_screen.dart
+++ b/bloc_flutter/lib/screens/home_screen.dart
@@ -1,7 +1,3 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'dart:async';
import 'package:bloc_flutter_sample/dependency_injection.dart';
@@ -14,18 +10,19 @@ import 'package:bloc_flutter_sample/widgets/todo_list.dart';
import 'package:bloc_flutter_sample/widgets/todos_bloc_provider.dart';
import 'package:blocs/blocs.dart';
import 'package:flutter/material.dart';
-import 'package:todos_app_core/todos_app_core.dart';
-import 'package:meta/meta.dart';
import 'package:rxdart/rxdart.dart';
+import 'package:todos_app_core/todos_app_core.dart';
import 'package:todos_repository_core/todos_repository_core.dart';
enum AppTab { todos, stats }
class HomeScreen extends StatefulWidget {
- final UserRepository repository;
+ final UserRepository userRepository;
- HomeScreen({@required this.repository})
- : super(key: ArchSampleKeys.homeScreen);
+ const HomeScreen({
+ super.key = ArchSampleKeys.homeScreen,
+ required this.userRepository,
+ });
@override
State createState() {
@@ -34,14 +31,14 @@ class HomeScreen extends StatefulWidget {
}
class HomeScreenState extends State {
- UserBloc usersBloc;
- StreamController tabController;
+ late UserBloc usersBloc;
+ late StreamController tabController;
@override
void initState() {
super.initState();
- usersBloc = UserBloc(widget.repository);
+ usersBloc = UserBloc(widget.userRepository);
tabController = StreamController();
}
@@ -73,25 +70,23 @@ class HomeScreenState extends State {
),
body: userSnapshot.hasData
? activeTabSnapshot.data == AppTab.todos
- ? TodoList()
- : StatsCounter(
- buildBloc: () =>
- StatsBloc(Injector.of(context).todosInteractor),
- )
- : LoadingSpinner(
- key: ArchSampleKeys.todosLoading,
- ),
+ ? TodoList()
+ : StatsCounter(
+ buildBloc: () =>
+ StatsBloc(Injector.of(context).todosInteractor),
+ )
+ : LoadingSpinner(key: ArchSampleKeys.todosLoading),
floatingActionButton: FloatingActionButton(
key: ArchSampleKeys.addTodoFab,
onPressed: () {
Navigator.pushNamed(context, ArchSampleRoutes.addTodo);
},
- child: Icon(Icons.add),
tooltip: ArchSampleLocalizations.of(context).addTodo,
+ child: Icon(Icons.add),
),
bottomNavigationBar: BottomNavigationBar(
key: ArchSampleKeys.tabs,
- currentIndex: AppTab.values.indexOf(activeTabSnapshot.data),
+ currentIndex: AppTab.values.indexOf(activeTabSnapshot.data!),
onTap: (index) {
tabController.add(AppTab.values[index]);
},
@@ -103,11 +98,9 @@ class HomeScreenState extends State {
? ArchSampleKeys.statsTab
: ArchSampleKeys.todoTab,
),
- title: Text(
- tab == AppTab.stats
- ? ArchSampleLocalizations.of(context).stats
- : ArchSampleLocalizations.of(context).todos,
- ),
+ label: tab == AppTab.stats
+ ? ArchSampleLocalizations.of(context).stats
+ : ArchSampleLocalizations.of(context).todos,
);
}).toList(),
),
@@ -141,10 +134,7 @@ class HomeScreenState extends State {
todosBloc.allComplete,
todosBloc.hasCompletedTodos,
(allComplete, hasCompletedTodos) {
- return ExtraActionsButtonViewModel(
- allComplete,
- hasCompletedTodos,
- );
+ return ExtraActionsButtonViewModel(allComplete, hasCompletedTodos);
},
),
builder: (context, snapshot) {
@@ -160,7 +150,7 @@ class HomeScreenState extends State {
},
);
},
- )
+ ),
];
}
}
diff --git a/bloc_flutter/lib/widgets/extra_actions_button.dart b/bloc_flutter/lib/widgets/extra_actions_button.dart
index 1f6955b7..809249e8 100644
--- a/bloc_flutter/lib/widgets/extra_actions_button.dart
+++ b/bloc_flutter/lib/widgets/extra_actions_button.dart
@@ -1,7 +1,3 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:flutter/material.dart';
import 'package:todos_app_core/todos_app_core.dart';
@@ -10,17 +6,16 @@ class ExtraActionsButton extends StatelessWidget {
final bool allComplete;
final bool hasCompletedTodos;
- ExtraActionsButton({
- this.onSelected,
+ const ExtraActionsButton({
+ super.key = ArchSampleKeys.extraActionsButton,
+ required this.onSelected,
this.allComplete = false,
this.hasCompletedTodos = true,
- Key key,
- }) : super(key: key);
+ });
@override
Widget build(BuildContext context) {
return PopupMenuButton(
- key: ArchSampleKeys.extraActionsButton,
onSelected: onSelected,
itemBuilder: (BuildContext context) {
return >[
@@ -36,9 +31,7 @@ class ExtraActionsButton extends StatelessWidget {
PopupMenuItem(
key: ArchSampleKeys.clearCompleted,
value: ExtraAction.clearCompleted,
- child: Text(
- ArchSampleLocalizations.of(context).clearCompleted,
- ),
+ child: Text(ArchSampleLocalizations.of(context).clearCompleted),
),
];
},
diff --git a/bloc_flutter/lib/widgets/filter_button.dart b/bloc_flutter/lib/widgets/filter_button.dart
index e88a4e43..26a79e98 100644
--- a/bloc_flutter/lib/widgets/filter_button.dart
+++ b/bloc_flutter/lib/widgets/filter_button.dart
@@ -1,7 +1,3 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:blocs/blocs.dart';
import 'package:flutter/material.dart';
import 'package:todos_app_core/todos_app_core.dart';
@@ -11,15 +7,18 @@ class FilterButton extends StatelessWidget {
final VisibilityFilter activeFilter;
final bool isActive;
- FilterButton({this.onSelected, this.activeFilter, this.isActive, Key key})
- : super(key: key);
+ const FilterButton({
+ super.key = ArchSampleKeys.filterButton,
+ required this.onSelected,
+ required this.activeFilter,
+ required this.isActive,
+ });
@override
Widget build(BuildContext context) {
- final theme = Theme.of(context);
- final defaultStyle = theme.textTheme.body1;
- final activeStyle = theme.textTheme.body1.copyWith(
- color: theme.accentColor,
+ final defaultStyle = Theme.of(context).textTheme.bodyMedium!;
+ final activeStyle = defaultStyle.copyWith(
+ color: Theme.of(context).colorScheme.secondary,
);
final button = _Button(
onSelected: onSelected,
@@ -38,12 +37,11 @@ class FilterButton extends StatelessWidget {
class _Button extends StatelessWidget {
const _Button({
- Key key,
- @required this.onSelected,
- @required this.activeFilter,
- @required this.activeStyle,
- @required this.defaultStyle,
- }) : super(key: key);
+ required this.onSelected,
+ required this.activeFilter,
+ required this.activeStyle,
+ required this.defaultStyle,
+ });
final PopupMenuItemSelected onSelected;
final VisibilityFilter activeFilter;
@@ -53,7 +51,6 @@ class _Button extends StatelessWidget {
@override
Widget build(BuildContext context) {
return PopupMenuButton(
- key: ArchSampleKeys.filterButton,
tooltip: ArchSampleLocalizations.of(context).filterTodos,
onSelected: onSelected,
itemBuilder: (BuildContext context) {
diff --git a/bloc_flutter/lib/widgets/loading.dart b/bloc_flutter/lib/widgets/loading.dart
index 0fa4416a..68c0ec55 100644
--- a/bloc_flutter/lib/widgets/loading.dart
+++ b/bloc_flutter/lib/widgets/loading.dart
@@ -1,16 +1,10 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:flutter/material.dart';
class LoadingSpinner extends StatelessWidget {
- LoadingSpinner({Key key}) : super(key: key);
+ const LoadingSpinner({super.key});
@override
Widget build(BuildContext context) {
- return Center(
- child: CircularProgressIndicator(),
- );
+ return Center(child: CircularProgressIndicator());
}
}
diff --git a/bloc_flutter/lib/widgets/stats_counter.dart b/bloc_flutter/lib/widgets/stats_counter.dart
index 8780d73a..94467c42 100644
--- a/bloc_flutter/lib/widgets/stats_counter.dart
+++ b/bloc_flutter/lib/widgets/stats_counter.dart
@@ -1,18 +1,14 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:blocs/blocs.dart';
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:todos_app_core/todos_app_core.dart';
class StatsCounter extends StatefulWidget {
final StatsBloc Function() buildBloc;
- StatsCounter({Key key, @required this.buildBloc})
- : super(key: key ?? ArchSampleKeys.statsCounter);
+ const StatsCounter({
+ super.key = ArchSampleKeys.statsCounter,
+ required this.buildBloc,
+ });
@override
StatsCounterState createState() {
@@ -21,7 +17,7 @@ class StatsCounter extends StatefulWidget {
}
class StatsCounterState extends State {
- StatsBloc bloc;
+ late StatsBloc bloc;
@override
void initState() {
@@ -39,7 +35,7 @@ class StatsCounterState extends State {
padding: EdgeInsets.only(bottom: 8.0),
child: Text(
ArchSampleLocalizations.of(context).completedTodos,
- style: Theme.of(context).textTheme.title,
+ style: Theme.of(context).textTheme.titleLarge,
),
),
Padding(
@@ -49,7 +45,7 @@ class StatsCounterState extends State {
builder: (context, snapshot) => Text(
'${snapshot.data ?? 0}',
key: ArchSampleKeys.statsNumCompleted,
- style: Theme.of(context).textTheme.subhead,
+ style: Theme.of(context).textTheme.titleMedium,
),
),
),
@@ -57,7 +53,7 @@ class StatsCounterState extends State {
padding: EdgeInsets.only(bottom: 8.0),
child: Text(
ArchSampleLocalizations.of(context).activeTodos,
- style: Theme.of(context).textTheme.title,
+ style: Theme.of(context).textTheme.titleLarge,
),
),
Padding(
@@ -68,11 +64,11 @@ class StatsCounterState extends State {
return Text(
'${snapshot.data ?? 0}',
key: ArchSampleKeys.statsNumActive,
- style: Theme.of(context).textTheme.subhead,
+ style: Theme.of(context).textTheme.titleMedium,
);
},
),
- )
+ ),
],
),
);
diff --git a/bloc_flutter/lib/widgets/todo_item.dart b/bloc_flutter/lib/widgets/todo_item.dart
index 716f87a5..69250885 100644
--- a/bloc_flutter/lib/widgets/todo_item.dart
+++ b/bloc_flutter/lib/widgets/todo_item.dart
@@ -1,23 +1,19 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:blocs/blocs.dart';
-import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:todos_app_core/todos_app_core.dart';
class TodoItem extends StatelessWidget {
final DismissDirectionCallback onDismissed;
final GestureTapCallback onTap;
- final ValueChanged onCheckboxChanged;
+ final ValueChanged onCheckboxChanged;
final Todo todo;
- TodoItem({
- @required this.onDismissed,
- @required this.onTap,
- @required this.onCheckboxChanged,
- @required this.todo,
+ const TodoItem({
+ super.key,
+ required this.onDismissed,
+ required this.onTap,
+ required this.onCheckboxChanged,
+ required this.todo,
});
@override
@@ -35,14 +31,14 @@ class TodoItem extends StatelessWidget {
title: Text(
todo.task,
key: ArchSampleKeys.todoItemTask(todo.id),
- style: Theme.of(context).textTheme.title,
+ style: Theme.of(context).textTheme.titleLarge,
),
subtitle: Text(
todo.note,
key: ArchSampleKeys.todoItemNote(todo.id),
maxLines: 1,
overflow: TextOverflow.ellipsis,
- style: Theme.of(context).textTheme.subhead,
+ style: Theme.of(context).textTheme.titleMedium,
),
),
);
diff --git a/bloc_flutter/lib/widgets/todo_list.dart b/bloc_flutter/lib/widgets/todo_list.dart
index e4f24023..77e94689 100644
--- a/bloc_flutter/lib/widgets/todo_list.dart
+++ b/bloc_flutter/lib/widgets/todo_list.dart
@@ -1,7 +1,3 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:bloc_flutter_sample/dependency_injection.dart';
import 'package:bloc_flutter_sample/screens/detail_screen.dart';
import 'package:bloc_flutter_sample/widgets/loading.dart';
@@ -12,14 +8,14 @@ import 'package:flutter/material.dart';
import 'package:todos_app_core/todos_app_core.dart';
class TodoList extends StatelessWidget {
- TodoList({Key key}) : super(key: key);
+ const TodoList({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder>(
stream: TodosBlocProvider.of(context).visibleTodos,
builder: (context, snapshot) => snapshot.hasData
- ? _buildList(snapshot.data)
+ ? _buildList(snapshot.data!)
: LoadingSpinner(key: ArchSampleKeys.todosLoading),
);
}
@@ -37,26 +33,28 @@ class TodoList extends StatelessWidget {
_removeTodo(context, todo);
},
onTap: () {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (_) {
- return DetailScreen(
- todoId: todo.id,
- initBloc: () =>
- TodoBloc(Injector.of(context).todosInteractor),
- );
- },
- ),
- ).then((todo) {
- if (todo is Todo) {
- _showUndoSnackbar(context, todo);
- }
- });
+ Navigator.of(context)
+ .push(
+ MaterialPageRoute(
+ builder: (_) {
+ return DetailScreen(
+ todoId: todo.id,
+ initBloc: () =>
+ TodoBloc(Injector.of(context).todosInteractor),
+ );
+ },
+ ),
+ )
+ .then((todo) {
+ if (todo is Todo && context.mounted) {
+ _showUndoSnackbar(context, todo);
+ }
+ });
},
onCheckboxChanged: (complete) {
- TodosBlocProvider.of(context)
- .updateTodo
- .add(todo.copyWith(complete: !todo.complete));
+ TodosBlocProvider.of(
+ context,
+ ).updateTodo.add(todo.copyWith(complete: !todo.complete));
},
);
},
@@ -87,6 +85,6 @@ class TodoList extends StatelessWidget {
),
);
- Scaffold.of(context).showSnackBar(snackBar);
+ ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
diff --git a/bloc_flutter/lib/widgets/todos_bloc_provider.dart b/bloc_flutter/lib/widgets/todos_bloc_provider.dart
index 37103758..dfbf41b3 100644
--- a/bloc_flutter/lib/widgets/todos_bloc_provider.dart
+++ b/bloc_flutter/lib/widgets/todos_bloc_provider.dart
@@ -1,29 +1,23 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:blocs/blocs.dart';
-import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class TodosBlocProvider extends StatefulWidget {
final Widget child;
final TodosListBloc bloc;
- TodosBlocProvider({Key key, @required this.child, @required this.bloc})
- : super(key: key);
+ const TodosBlocProvider({super.key, required this.child, required this.bloc});
@override
- _TodosBlocProviderState createState() => _TodosBlocProviderState();
+ TodosBlocProviderState createState() => TodosBlocProviderState();
static TodosListBloc of(BuildContext context) {
return context
- .dependOnInheritedWidgetOfExactType<_TodosBlocProvider>()
+ .dependOnInheritedWidgetOfExactType<_TodosBlocProvider>()!
.bloc;
}
}
-class _TodosBlocProviderState extends State {
+class TodosBlocProviderState extends State {
@override
Widget build(BuildContext context) {
return _TodosBlocProvider(bloc: widget.bloc, child: widget.child);
@@ -39,11 +33,7 @@ class _TodosBlocProviderState extends State {
class _TodosBlocProvider extends InheritedWidget {
final TodosListBloc bloc;
- _TodosBlocProvider({
- Key key,
- @required this.bloc,
- @required Widget child,
- }) : super(key: key, child: child);
+ const _TodosBlocProvider({required this.bloc, required super.child});
@override
bool updateShouldNotify(_TodosBlocProvider old) => bloc != old.bloc;
diff --git a/bloc_flutter/linux/.gitignore b/bloc_flutter/linux/.gitignore
new file mode 100644
index 00000000..d3896c98
--- /dev/null
+++ b/bloc_flutter/linux/.gitignore
@@ -0,0 +1 @@
+flutter/ephemeral
diff --git a/bloc_flutter/linux/CMakeLists.txt b/bloc_flutter/linux/CMakeLists.txt
new file mode 100644
index 00000000..6f4ebf4e
--- /dev/null
+++ b/bloc_flutter/linux/CMakeLists.txt
@@ -0,0 +1,128 @@
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.13)
+project(runner LANGUAGES CXX)
+
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
+set(BINARY_NAME "bloc_flutter_sample")
+# The unique GTK application identifier for this application. See:
+# https://wiki.gnome.org/HowDoI/ChooseApplicationID
+set(APPLICATION_ID "com.example.bloc_flutter_sample")
+
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(SET CMP0063 NEW)
+
+# Load bundled libraries from the lib/ directory relative to the binary.
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Root filesystem for cross-building.
+if(FLUTTER_TARGET_PLATFORM_SYSROOT)
+ set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+endif()
+
+# Define build configuration options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+# Application build; see runner/CMakeLists.txt.
+add_subdirectory("runner")
+
+# Run the Flutter tool portions of the build. This must not be removed.
+add_dependencies(${BINARY_NAME} flutter_assemble)
+
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
+ install(FILES "${bundled_library}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endforeach(bundled_library)
+
+# Copy the native assets provided by the build.dart from all packages.
+set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
+install(DIRECTORY "${NATIVE_ASSETS_DIR}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/bloc_flutter/linux/flutter/CMakeLists.txt b/bloc_flutter/linux/flutter/CMakeLists.txt
new file mode 100644
index 00000000..d5bd0164
--- /dev/null
+++ b/bloc_flutter/linux/flutter/CMakeLists.txt
@@ -0,0 +1,88 @@
+# This file controls Flutter-level build steps. It should not be edited.
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/bloc_flutter/linux/flutter/generated_plugin_registrant.cc b/bloc_flutter/linux/flutter/generated_plugin_registrant.cc
new file mode 100644
index 00000000..e71a16d2
--- /dev/null
+++ b/bloc_flutter/linux/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,11 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#include "generated_plugin_registrant.h"
+
+
+void fl_register_plugins(FlPluginRegistry* registry) {
+}
diff --git a/bloc_flutter/linux/flutter/generated_plugin_registrant.h b/bloc_flutter/linux/flutter/generated_plugin_registrant.h
new file mode 100644
index 00000000..e0f0a47b
--- /dev/null
+++ b/bloc_flutter/linux/flutter/generated_plugin_registrant.h
@@ -0,0 +1,15 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void fl_register_plugins(FlPluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/bloc_flutter/linux/flutter/generated_plugins.cmake b/bloc_flutter/linux/flutter/generated_plugins.cmake
new file mode 100644
index 00000000..2e1de87a
--- /dev/null
+++ b/bloc_flutter/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,23 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+list(APPEND FLUTTER_FFI_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
+
+foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
+endforeach(ffi_plugin)
diff --git a/bloc_flutter/linux/runner/CMakeLists.txt b/bloc_flutter/linux/runner/CMakeLists.txt
new file mode 100644
index 00000000..e97dabc7
--- /dev/null
+++ b/bloc_flutter/linux/runner/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.13)
+project(runner LANGUAGES CXX)
+
+# Define the application target. To change its name, change BINARY_NAME in the
+# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
+# work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add preprocessor definitions for the application ID.
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Add dependency libraries. Add any application-specific dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
diff --git a/bloc_flutter/linux/runner/main.cc b/bloc_flutter/linux/runner/main.cc
new file mode 100644
index 00000000..e7c5c543
--- /dev/null
+++ b/bloc_flutter/linux/runner/main.cc
@@ -0,0 +1,6 @@
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/bloc_flutter/linux/runner/my_application.cc b/bloc_flutter/linux/runner/my_application.cc
new file mode 100644
index 00000000..4a792229
--- /dev/null
+++ b/bloc_flutter/linux/runner/my_application.cc
@@ -0,0 +1,144 @@
+#include "my_application.h"
+
+#include
+#ifdef GDK_WINDOWING_X11
+#include
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Called when first Flutter frame received.
+static void first_frame_cb(MyApplication* self, FlView *view)
+{
+ gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view)));
+}
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "bloc_flutter_sample");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "bloc_flutter_sample");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ GdkRGBA background_color;
+ // Background defaults to black, override it here if necessary, e.g. #00000000 for transparent.
+ gdk_rgba_parse(&background_color, "#000000");
+ fl_view_set_background_color(view, &background_color);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ // Show the window when Flutter renders.
+ // Requires the view to be realized so we can start rendering.
+ g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb), self);
+ gtk_widget_realize(GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GApplication::startup.
+static void my_application_startup(GApplication* application) {
+ //MyApplication* self = MY_APPLICATION(object);
+
+ // Perform any actions required at application startup.
+
+ G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
+}
+
+// Implements GApplication::shutdown.
+static void my_application_shutdown(GApplication* application) {
+ //MyApplication* self = MY_APPLICATION(object);
+
+ // Perform any actions required at application shutdown.
+
+ G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
+ G_APPLICATION_CLASS(klass)->startup = my_application_startup;
+ G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ // Set the program name to the application ID, which helps various systems
+ // like GTK and desktop environments map this running application to its
+ // corresponding .desktop file. This ensures better integration by allowing
+ // the application to be recognized beyond its binary name.
+ g_set_prgname(APPLICATION_ID);
+
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID,
+ "flags", G_APPLICATION_NON_UNIQUE,
+ nullptr));
+}
diff --git a/bloc_flutter/linux/runner/my_application.h b/bloc_flutter/linux/runner/my_application.h
new file mode 100644
index 00000000..72271d5e
--- /dev/null
+++ b/bloc_flutter/linux/runner/my_application.h
@@ -0,0 +1,18 @@
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/bloc_flutter/macos/.gitignore b/bloc_flutter/macos/.gitignore
new file mode 100644
index 00000000..746adbb6
--- /dev/null
+++ b/bloc_flutter/macos/.gitignore
@@ -0,0 +1,7 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/dgph
+**/xcuserdata/
diff --git a/bloc_flutter/macos/Flutter/Flutter-Debug.xcconfig b/bloc_flutter/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 00000000..4b81f9b2
--- /dev/null
+++ b/bloc_flutter/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/bloc_flutter/macos/Flutter/Flutter-Release.xcconfig b/bloc_flutter/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 00000000..5caa9d15
--- /dev/null
+++ b/bloc_flutter/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/bloc_flutter/macos/Flutter/GeneratedPluginRegistrant.swift b/bloc_flutter/macos/Flutter/GeneratedPluginRegistrant.swift
new file mode 100644
index 00000000..b8e2b22f
--- /dev/null
+++ b/bloc_flutter/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -0,0 +1,14 @@
+//
+// Generated file. Do not edit.
+//
+
+import FlutterMacOS
+import Foundation
+
+import path_provider_foundation
+import shared_preferences_foundation
+
+func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+ PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
+ SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
+}
diff --git a/bloc_flutter/macos/Podfile b/bloc_flutter/macos/Podfile
new file mode 100644
index 00000000..ff5ddb3b
--- /dev/null
+++ b/bloc_flutter/macos/Podfile
@@ -0,0 +1,42 @@
+platform :osx, '10.15'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_macos_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+
+ flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_macos_build_settings(target)
+ end
+end
diff --git a/bloc_flutter/macos/Runner.xcodeproj/project.pbxproj b/bloc_flutter/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..5a57ad02
--- /dev/null
+++ b/bloc_flutter/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,705 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC10EC2044A3C60003C045;
+ remoteInfo = Runner;
+ };
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
+ 33CC10ED2044A3C60003C045 /* bloc_flutter_sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "bloc_flutter_sample.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 331C80D2294CF70F00263BE5 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 331C80D6294CF71000263BE5 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 331C80D7294CF71000263BE5 /* RunnerTests.swift */,
+ );
+ path = RunnerTests;
+ sourceTree = "";
+ };
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 331C80D6294CF71000263BE5 /* RunnerTests */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* bloc_flutter_sample.app */,
+ 331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 331C80D4294CF70F00263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ 331C80D1294CF70F00263BE5 /* Sources */,
+ 331C80D2294CF70F00263BE5 /* Frameworks */,
+ 331C80D3294CF70F00263BE5 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C80DA294CF71000263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* bloc_flutter_sample.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = YES;
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 1510;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 331C80D4294CF70F00263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 33CC10EC2044A3C60003C045;
+ };
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 331C80D4294CF70F00263BE5 /* RunnerTests */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 331C80D3294CF70F00263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 331C80D1294CF70F00263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC10EC2044A3C60003C045 /* Runner */;
+ targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
+ };
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 331C80DB294CF71000263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutterSample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bloc_flutter_sample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bloc_flutter_sample";
+ };
+ name = Debug;
+ };
+ 331C80DC294CF71000263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutterSample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bloc_flutter_sample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bloc_flutter_sample";
+ };
+ name = Release;
+ };
+ 331C80DD294CF71000263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutterSample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bloc_flutter_sample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bloc_flutter_sample";
+ };
+ name = Profile;
+ };
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C80DB294CF71000263BE5 /* Debug */,
+ 331C80DC294CF71000263BE5 /* Release */,
+ 331C80DD294CF71000263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/bloc_flutter/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/bloc_flutter/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/bloc_flutter/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/bloc_flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/bloc_flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 00000000..0c6d28c2
--- /dev/null
+++ b/bloc_flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/built_redux/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/bloc_flutter/macos/Runner.xcworkspace/contents.xcworkspacedata
similarity index 100%
rename from built_redux/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
rename to bloc_flutter/macos/Runner.xcworkspace/contents.xcworkspacedata
diff --git a/bloc_flutter/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/bloc_flutter/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/bloc_flutter/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/bloc_flutter/macos/Runner/AppDelegate.swift b/bloc_flutter/macos/Runner/AppDelegate.swift
new file mode 100644
index 00000000..b3c17614
--- /dev/null
+++ b/bloc_flutter/macos/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import Cocoa
+import FlutterMacOS
+
+@main
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+
+ override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..a2ec33f1
--- /dev/null
+++ b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 00000000..82b6f9d9
Binary files /dev/null and b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ
diff --git a/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 00000000..13b35eba
Binary files /dev/null and b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ
diff --git a/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 00000000..0a3f5fa4
Binary files /dev/null and b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ
diff --git a/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 00000000..bdb57226
Binary files /dev/null and b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ
diff --git a/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 00000000..f083318e
Binary files /dev/null and b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ
diff --git a/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 00000000..326c0e72
Binary files /dev/null and b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ
diff --git a/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 00000000..2f1632cf
Binary files /dev/null and b/bloc_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ
diff --git a/bloc_flutter/macos/Runner/Base.lproj/MainMenu.xib b/bloc_flutter/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 00000000..80e867a4
--- /dev/null
+++ b/bloc_flutter/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bloc_flutter/macos/Runner/Configs/AppInfo.xcconfig b/bloc_flutter/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 00000000..50b414d2
--- /dev/null
+++ b/bloc_flutter/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = bloc_flutter_sample
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = com.example.blocFlutterSample
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved.
diff --git a/bloc_flutter/macos/Runner/Configs/Debug.xcconfig b/bloc_flutter/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 00000000..36b0fd94
--- /dev/null
+++ b/bloc_flutter/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/bloc_flutter/macos/Runner/Configs/Release.xcconfig b/bloc_flutter/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 00000000..dff4f495
--- /dev/null
+++ b/bloc_flutter/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/bloc_flutter/macos/Runner/Configs/Warnings.xcconfig b/bloc_flutter/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 00000000..42bcbf47
--- /dev/null
+++ b/bloc_flutter/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/bloc_flutter/macos/Runner/DebugProfile.entitlements b/bloc_flutter/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 00000000..dddb8a30
--- /dev/null
+++ b/bloc_flutter/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.network.server
+
+
+
diff --git a/bloc_flutter/macos/Runner/Info.plist b/bloc_flutter/macos/Runner/Info.plist
new file mode 100644
index 00000000..4789daa6
--- /dev/null
+++ b/bloc_flutter/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSHumanReadableCopyright
+ $(PRODUCT_COPYRIGHT)
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/bloc_flutter/macos/Runner/MainFlutterWindow.swift b/bloc_flutter/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 00000000..3cc05eb2
--- /dev/null
+++ b/bloc_flutter/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,15 @@
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/bloc_flutter/macos/Runner/Release.entitlements b/bloc_flutter/macos/Runner/Release.entitlements
new file mode 100644
index 00000000..852fa1a4
--- /dev/null
+++ b/bloc_flutter/macos/Runner/Release.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+
diff --git a/bloc_flutter/macos/RunnerTests/RunnerTests.swift b/bloc_flutter/macos/RunnerTests/RunnerTests.swift
new file mode 100644
index 00000000..61f3bd1f
--- /dev/null
+++ b/bloc_flutter/macos/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import Cocoa
+import FlutterMacOS
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/bloc_flutter/pubspec.yaml b/bloc_flutter/pubspec.yaml
index 559b2d60..13e379fa 100644
--- a/bloc_flutter/pubspec.yaml
+++ b/bloc_flutter/pubspec.yaml
@@ -12,33 +12,34 @@ description: A new Flutter project.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
+publish_to: "none"
environment:
- sdk: ">=2.1.0 <3.0.0"
+ sdk: ^3.9.0
dependencies:
flutter:
sdk: flutter
- todos_repository_local_storage:
- path: ../todos_repository_local_storage
- firebase_flutter_repository:
- path: ../firebase_flutter_repository
todos_app_core:
path: ../todos_app_core
+ todos_repository_core:
+ path: ../todos_repository_core
+ todos_repository_local_storage:
+ path: ../todos_repository_local_storage
blocs:
path: ../blocs
- rxdart: ^0.23.1
- key_value_store_flutter:
- key_value_store_web:
+ rxdart: ^0.28.0
shared_preferences:
dev_dependencies:
+ flutter_lints:
flutter_test:
sdk: flutter
- flutter_driver:
+ integration_test:
sdk: flutter
test:
mockito:
+ build_runner:
integration_tests:
path: ../integration_tests
@@ -47,7 +48,6 @@ dev_dependencies:
# The following section is specific to Flutter.
flutter:
-
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
diff --git a/bloc_flutter/test/home_screen_test.dart b/bloc_flutter/test/home_screen_test.dart
new file mode 100644
index 00000000..55bcf018
--- /dev/null
+++ b/bloc_flutter/test/home_screen_test.dart
@@ -0,0 +1,171 @@
+import 'package:bloc_flutter_sample/anonymous_user_repository.dart';
+import 'package:bloc_flutter_sample/dependency_injection.dart';
+import 'package:bloc_flutter_sample/localization.dart';
+import 'package:bloc_flutter_sample/screens/home_screen.dart';
+import 'package:bloc_flutter_sample/widgets/todos_bloc_provider.dart';
+import 'package:blocs/blocs.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+import 'package:todos_repository_core/todos_repository_core.dart';
+
+import 'home_screen_test.mocks.dart';
+
+@GenerateNiceMocks([MockSpec(), MockSpec()])
+void main() {
+ group('HomeScreen', () {
+ final todoListFinder = find.byKey(ArchSampleKeys.todoList);
+ final todoItem1Finder = find.byKey(ArchSampleKeys.todoItem('1'));
+ final todoItem2Finder = find.byKey(ArchSampleKeys.todoItem('2'));
+ final todoItem3Finder = find.byKey(ArchSampleKeys.todoItem('3'));
+
+ testWidgets('should render loading indicator at first', (tester) async {
+ await tester.pumpWidget(
+ _TestWidget(
+ todosInteractor: MockTodosInteractor(),
+ userRepository: AnonymousUserRepository(),
+ ),
+ );
+ await tester.pump(Duration.zero);
+
+ expect(find.byKey(ArchSampleKeys.todosLoading), findsOneWidget);
+ });
+
+ testWidgets('should display a list after loading todos', (tester) async {
+ final handle = tester.ensureSemantics();
+ final interactor = MockTodosInteractor();
+
+ when(
+ interactor.todos,
+ ).thenAnswer((_) => Stream.fromIterable([_TestWidget._defaultTodos]));
+
+ await tester.pumpWidget(
+ _TestWidget(
+ todosInteractor: interactor,
+ userRepository: AnonymousUserRepository(),
+ ),
+ );
+ await tester.pumpAndSettle();
+
+ final checkbox1 = find.descendant(
+ of: find.byKey(ArchSampleKeys.todoItemCheckbox('1')),
+ matching: find.byType(Focus),
+ );
+ final checkbox2 = find.descendant(
+ of: find.byKey(ArchSampleKeys.todoItemCheckbox('2')),
+ matching: find.byType(Focus),
+ );
+ final checkbox3 = find.descendant(
+ of: find.byKey(ArchSampleKeys.todoItemCheckbox('3')),
+ matching: find.byType(Focus),
+ );
+
+ expect(todoListFinder, findsOneWidget);
+ expect(todoItem1Finder, findsOneWidget);
+ expect(find.text('T1'), findsOneWidget);
+ expect(find.text('N1'), findsOneWidget);
+ expect(tester.getSemantics(checkbox1), isChecked(false));
+ expect(todoItem2Finder, findsOneWidget);
+ expect(find.text('T2'), findsOneWidget);
+ expect(tester.getSemantics(checkbox2), isChecked(false));
+ expect(todoItem3Finder, findsOneWidget);
+ expect(find.text('T3'), findsOneWidget);
+ expect(tester.getSemantics(checkbox3), isChecked(true));
+
+ handle.dispose();
+ });
+
+ testWidgets('should remove todos using a dismissible', (tester) async {
+ final interactor = MockTodosInteractor();
+
+ when(
+ interactor.todos,
+ ).thenAnswer((_) => Stream.fromIterable([_TestWidget._defaultTodos]));
+
+ await tester.pumpWidget(
+ _TestWidget(
+ todosInteractor: interactor,
+ userRepository: AnonymousUserRepository(),
+ ),
+ );
+ await tester.pumpAndSettle();
+ await tester.drag(todoItem1Finder, Offset(-1000, 0));
+ await tester.pumpAndSettle(Duration(seconds: 5));
+
+ expect(todoItem1Finder, findsNothing);
+ expect(todoItem2Finder, findsOneWidget);
+ expect(todoItem3Finder, findsOneWidget);
+ });
+
+ testWidgets('should display stats when switching tabs', (tester) async {
+ final interactor = MockTodosInteractor();
+
+ when(
+ interactor.todos,
+ ).thenAnswer((_) => Stream.fromIterable([_TestWidget._defaultTodos]));
+
+ await tester.pumpWidget(
+ _TestWidget(
+ todosInteractor: interactor,
+ userRepository: AnonymousUserRepository(),
+ ),
+ );
+ await tester.pumpAndSettle();
+ await tester.tap(find.byKey(ArchSampleKeys.statsTab));
+ await tester.pump();
+
+ expect(find.byKey(ArchSampleKeys.statsNumActive), findsOneWidget);
+ expect(find.byKey(ArchSampleKeys.statsNumActive), findsOneWidget);
+ });
+ });
+}
+
+class _TestWidget extends StatelessWidget {
+ const _TestWidget({
+ required this.todosInteractor,
+ required this.userRepository,
+ });
+
+ final TodosInteractor todosInteractor;
+ final UserRepository userRepository;
+
+ @override
+ Widget build(BuildContext context) {
+ return Injector(
+ todosInteractor: todosInteractor,
+ userRepository: userRepository,
+ child: TodosBlocProvider(
+ bloc: TodosListBloc(todosInteractor),
+ child: MaterialApp(
+ localizationsDelegates: [
+ BlocLocalizationsDelegate(),
+ ArchSampleLocalizationsDelegate(),
+ ],
+ home: HomeScreen(userRepository: userRepository),
+ ),
+ ),
+ );
+ }
+
+ static List get _defaultTodos {
+ return [
+ Todo('T1', id: '1', note: 'N1'),
+ Todo('T2', id: '2'),
+ Todo('T3', id: '3', complete: true),
+ ];
+ }
+}
+
+Matcher isChecked(bool isChecked) {
+ return matchesSemantics(
+ isChecked: isChecked,
+ hasTapAction: true,
+ hasFocusAction: true,
+ hasCheckedState: true,
+ isFocusable: true,
+ hasEnabledState: true,
+ isEnabled: true,
+ );
+}
diff --git a/bloc_flutter/test/home_screen_test.mocks.dart b/bloc_flutter/test/home_screen_test.mocks.dart
new file mode 100644
index 00000000..2c51ada1
--- /dev/null
+++ b/bloc_flutter/test/home_screen_test.mocks.dart
@@ -0,0 +1,156 @@
+// Mocks generated by Mockito 5.4.6 from annotations
+// in bloc_flutter_sample/test/home_screen_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i4;
+
+import 'package:blocs/blocs.dart' as _i3;
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:todos_repository_core/todos_repository_core.dart' as _i2;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: deprecated_member_use
+// ignore_for_file: deprecated_member_use_from_same_package
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: must_be_immutable
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakeReactiveTodosRepository_0 extends _i1.SmartFake
+ implements _i2.ReactiveTodosRepository {
+ _FakeReactiveTodosRepository_0(Object parent, Invocation parentInvocation)
+ : super(parent, parentInvocation);
+}
+
+class _FakeUserEntity_1 extends _i1.SmartFake implements _i2.UserEntity {
+ _FakeUserEntity_1(Object parent, Invocation parentInvocation)
+ : super(parent, parentInvocation);
+}
+
+/// A class which mocks [TodosInteractor].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTodosInteractor extends _i1.Mock implements _i3.TodosInteractor {
+ @override
+ _i2.ReactiveTodosRepository get repository =>
+ (super.noSuchMethod(
+ Invocation.getter(#repository),
+ returnValue: _FakeReactiveTodosRepository_0(
+ this,
+ Invocation.getter(#repository),
+ ),
+ returnValueForMissingStub: _FakeReactiveTodosRepository_0(
+ this,
+ Invocation.getter(#repository),
+ ),
+ )
+ as _i2.ReactiveTodosRepository);
+
+ @override
+ _i4.Stream> get todos =>
+ (super.noSuchMethod(
+ Invocation.getter(#todos),
+ returnValue: _i4.Stream>.empty(),
+ returnValueForMissingStub: _i4.Stream>.empty(),
+ )
+ as _i4.Stream>);
+
+ @override
+ _i4.Stream get allComplete =>
+ (super.noSuchMethod(
+ Invocation.getter(#allComplete),
+ returnValue: _i4.Stream.empty(),
+ returnValueForMissingStub: _i4.Stream.empty(),
+ )
+ as _i4.Stream);
+
+ @override
+ _i4.Stream get hasCompletedTodos =>
+ (super.noSuchMethod(
+ Invocation.getter(#hasCompletedTodos),
+ returnValue: _i4.Stream.empty(),
+ returnValueForMissingStub: _i4.Stream.empty(),
+ )
+ as _i4.Stream);
+
+ @override
+ _i4.Stream<_i3.Todo> todo(String? id) =>
+ (super.noSuchMethod(
+ Invocation.method(#todo, [id]),
+ returnValue: _i4.Stream<_i3.Todo>.empty(),
+ returnValueForMissingStub: _i4.Stream<_i3.Todo>.empty(),
+ )
+ as _i4.Stream<_i3.Todo>);
+
+ @override
+ _i4.Future updateTodo(_i3.Todo? todo) =>
+ (super.noSuchMethod(
+ Invocation.method(#updateTodo, [todo]),
+ returnValue: _i4.Future.value(),
+ returnValueForMissingStub: _i4.Future.value(),
+ )
+ as _i4.Future);
+
+ @override
+ _i4.Future addNewTodo(_i3.Todo? todo) =>
+ (super.noSuchMethod(
+ Invocation.method(#addNewTodo, [todo]),
+ returnValue: _i4.Future.value(),
+ returnValueForMissingStub: _i4.Future.value(),
+ )
+ as _i4.Future);
+
+ @override
+ _i4.Future deleteTodo(String? id) =>
+ (super.noSuchMethod(
+ Invocation.method(#deleteTodo, [id]),
+ returnValue: _i4.Future.value(),
+ returnValueForMissingStub: _i4.Future.value(),
+ )
+ as _i4.Future);
+
+ @override
+ _i4.Future clearCompleted([dynamic _0]) =>
+ (super.noSuchMethod(
+ Invocation.method(#clearCompleted, [_0]),
+ returnValue: _i4.Future.value(),
+ returnValueForMissingStub: _i4.Future.value(),
+ )
+ as _i4.Future);
+
+ @override
+ _i4.Future> toggleAll([dynamic _0]) =>
+ (super.noSuchMethod(
+ Invocation.method(#toggleAll, [_0]),
+ returnValue: _i4.Future>.value([]),
+ returnValueForMissingStub: _i4.Future>.value(
+ [],
+ ),
+ )
+ as _i4.Future>);
+}
+
+/// A class which mocks [UserRepository].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockUserRepository extends _i1.Mock implements _i2.UserRepository {
+ @override
+ _i4.Future<_i2.UserEntity> login() =>
+ (super.noSuchMethod(
+ Invocation.method(#login, []),
+ returnValue: _i4.Future<_i2.UserEntity>.value(
+ _FakeUserEntity_1(this, Invocation.method(#login, [])),
+ ),
+ returnValueForMissingStub: _i4.Future<_i2.UserEntity>.value(
+ _FakeUserEntity_1(this, Invocation.method(#login, [])),
+ ),
+ )
+ as _i4.Future<_i2.UserEntity>);
+}
diff --git a/bloc_flutter/test_driver/integration_test.dart b/bloc_flutter/test_driver/integration_test.dart
new file mode 100644
index 00000000..b38629cc
--- /dev/null
+++ b/bloc_flutter/test_driver/integration_test.dart
@@ -0,0 +1,3 @@
+import 'package:integration_test/integration_test_driver.dart';
+
+Future main() => integrationDriver();
diff --git a/bloc_flutter/test_driver/todo_app.dart b/bloc_flutter/test_driver/todo_app.dart
deleted file mode 100644
index 5822c6d9..00000000
--- a/bloc_flutter/test_driver/todo_app.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
-// This line imports the extension
-import 'package:bloc_flutter_sample/main.dart' as app;
-import 'package:flutter_driver/driver_extension.dart';
-
-void main() {
- enableFlutterDriverExtension();
-
- app.main();
-}
diff --git a/bloc_flutter/test_driver/todo_app_test.dart b/bloc_flutter/test_driver/todo_app_test.dart
deleted file mode 100644
index ad93f5a5..00000000
--- a/bloc_flutter/test_driver/todo_app_test.dart
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
-import 'package:integration_tests/integration_tests.dart' as integration_tests;
-
-void main() {
- integration_tests.main();
-}
diff --git a/bloc_flutter/web/favicon.png b/bloc_flutter/web/favicon.png
new file mode 100644
index 00000000..8aaa46ac
Binary files /dev/null and b/bloc_flutter/web/favicon.png differ
diff --git a/bloc_flutter/web/icons/Icon-192.png b/bloc_flutter/web/icons/Icon-192.png
new file mode 100644
index 00000000..b749bfef
Binary files /dev/null and b/bloc_flutter/web/icons/Icon-192.png differ
diff --git a/bloc_flutter/web/icons/Icon-512.png b/bloc_flutter/web/icons/Icon-512.png
new file mode 100644
index 00000000..88cfd48d
Binary files /dev/null and b/bloc_flutter/web/icons/Icon-512.png differ
diff --git a/bloc_flutter/web/icons/Icon-maskable-192.png b/bloc_flutter/web/icons/Icon-maskable-192.png
new file mode 100644
index 00000000..eb9b4d76
Binary files /dev/null and b/bloc_flutter/web/icons/Icon-maskable-192.png differ
diff --git a/bloc_flutter/web/icons/Icon-maskable-512.png b/bloc_flutter/web/icons/Icon-maskable-512.png
new file mode 100644
index 00000000..d69c5669
Binary files /dev/null and b/bloc_flutter/web/icons/Icon-maskable-512.png differ
diff --git a/bloc_flutter/web/index.html b/bloc_flutter/web/index.html
index f99ea3a2..f9cba26a 100644
--- a/bloc_flutter/web/index.html
+++ b/bloc_flutter/web/index.html
@@ -1,10 +1,38 @@
+
+
+
- bloc_flutter
+
+
+
+
+
+
+
+
+
+
+
+
+ bloc_flutter_sample
+
-
+
diff --git a/bloc_flutter/web/manifest.json b/bloc_flutter/web/manifest.json
new file mode 100644
index 00000000..f6c0c9b8
--- /dev/null
+++ b/bloc_flutter/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "bloc_flutter_sample",
+ "short_name": "bloc_flutter_sample",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "A new Flutter project.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/bloc_flutter/windows/.gitignore b/bloc_flutter/windows/.gitignore
new file mode 100644
index 00000000..d492d0d9
--- /dev/null
+++ b/bloc_flutter/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/ephemeral/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/bloc_flutter/windows/CMakeLists.txt b/bloc_flutter/windows/CMakeLists.txt
new file mode 100644
index 00000000..7dd441e9
--- /dev/null
+++ b/bloc_flutter/windows/CMakeLists.txt
@@ -0,0 +1,108 @@
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.14)
+project(bloc_flutter_sample LANGUAGES CXX)
+
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
+set(BINARY_NAME "bloc_flutter_sample")
+
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(VERSION 3.14...3.25)
+
+# Define build configuration option.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+ endif()
+endif()
+# Define settings for the Profile build mode.
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build; see runner/CMakeLists.txt.
+add_subdirectory("runner")
+
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Copy the native assets provided by the build.dart from all packages.
+set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/")
+install(DIRECTORY "${NATIVE_ASSETS_DIR}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/bloc_flutter/windows/flutter/CMakeLists.txt b/bloc_flutter/windows/flutter/CMakeLists.txt
new file mode 100644
index 00000000..903f4899
--- /dev/null
+++ b/bloc_flutter/windows/flutter/CMakeLists.txt
@@ -0,0 +1,109 @@
+# This file controls Flutter-level build steps. It should not be edited.
+cmake_minimum_required(VERSION 3.14)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# Set fallback configurations for older versions of the flutter tool.
+if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
+ set(FLUTTER_TARGET_PLATFORM "windows-x64")
+endif()
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+ "flutter_texture_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ ${FLUTTER_TARGET_PLATFORM} $
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
diff --git a/bloc_flutter/windows/flutter/generated_plugin_registrant.cc b/bloc_flutter/windows/flutter/generated_plugin_registrant.cc
new file mode 100644
index 00000000..8b6d4680
--- /dev/null
+++ b/bloc_flutter/windows/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,11 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#include "generated_plugin_registrant.h"
+
+
+void RegisterPlugins(flutter::PluginRegistry* registry) {
+}
diff --git a/bloc_flutter/windows/flutter/generated_plugin_registrant.h b/bloc_flutter/windows/flutter/generated_plugin_registrant.h
new file mode 100644
index 00000000..dc139d85
--- /dev/null
+++ b/bloc_flutter/windows/flutter/generated_plugin_registrant.h
@@ -0,0 +1,15 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void RegisterPlugins(flutter::PluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/bloc_flutter/windows/flutter/generated_plugins.cmake b/bloc_flutter/windows/flutter/generated_plugins.cmake
new file mode 100644
index 00000000..b93c4c30
--- /dev/null
+++ b/bloc_flutter/windows/flutter/generated_plugins.cmake
@@ -0,0 +1,23 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+list(APPEND FLUTTER_FFI_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
+
+foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
+endforeach(ffi_plugin)
diff --git a/bloc_flutter/windows/runner/CMakeLists.txt b/bloc_flutter/windows/runner/CMakeLists.txt
new file mode 100644
index 00000000..394917c0
--- /dev/null
+++ b/bloc_flutter/windows/runner/CMakeLists.txt
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 3.14)
+project(runner LANGUAGES CXX)
+
+# Define the application target. To change its name, change BINARY_NAME in the
+# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
+# work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME} WIN32
+ "flutter_window.cpp"
+ "main.cpp"
+ "utils.cpp"
+ "win32_window.cpp"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+ "Runner.rc"
+ "runner.exe.manifest"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add preprocessor definitions for the build version.
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
+
+# Disable Windows macros that collide with C++ standard library functions.
+target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
+
+# Add dependency libraries and include directories. Add any application-specific
+# dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
+target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+
+# Run the Flutter tool portions of the build. This must not be removed.
+add_dependencies(${BINARY_NAME} flutter_assemble)
diff --git a/bloc_flutter/windows/runner/Runner.rc b/bloc_flutter/windows/runner/Runner.rc
new file mode 100644
index 00000000..aa6f7091
--- /dev/null
+++ b/bloc_flutter/windows/runner/Runner.rc
@@ -0,0 +1,121 @@
+// Microsoft Visual C++ generated resource script.
+//
+#pragma code_page(65001)
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP_ICON ICON "resources\\app_icon.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
+#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
+#else
+#define VERSION_AS_NUMBER 1,0,0,0
+#endif
+
+#if defined(FLUTTER_VERSION)
+#define VERSION_AS_STRING FLUTTER_VERSION
+#else
+#define VERSION_AS_STRING "1.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VERSION_AS_NUMBER
+ PRODUCTVERSION VERSION_AS_NUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "com.example" "\0"
+ VALUE "FileDescription", "bloc_flutter_sample" "\0"
+ VALUE "FileVersion", VERSION_AS_STRING "\0"
+ VALUE "InternalName", "bloc_flutter_sample" "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2025 com.example. All rights reserved." "\0"
+ VALUE "OriginalFilename", "bloc_flutter_sample.exe" "\0"
+ VALUE "ProductName", "bloc_flutter_sample" "\0"
+ VALUE "ProductVersion", VERSION_AS_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/bloc_flutter/windows/runner/flutter_window.cpp b/bloc_flutter/windows/runner/flutter_window.cpp
new file mode 100644
index 00000000..955ee303
--- /dev/null
+++ b/bloc_flutter/windows/runner/flutter_window.cpp
@@ -0,0 +1,71 @@
+#include "flutter_window.h"
+
+#include
+
+#include "flutter/generated_plugin_registrant.h"
+
+FlutterWindow::FlutterWindow(const flutter::DartProject& project)
+ : project_(project) {}
+
+FlutterWindow::~FlutterWindow() {}
+
+bool FlutterWindow::OnCreate() {
+ if (!Win32Window::OnCreate()) {
+ return false;
+ }
+
+ RECT frame = GetClientArea();
+
+ // The size here must match the window dimensions to avoid unnecessary surface
+ // creation / destruction in the startup path.
+ flutter_controller_ = std::make_unique(
+ frame.right - frame.left, frame.bottom - frame.top, project_);
+ // Ensure that basic setup of the controller was successful.
+ if (!flutter_controller_->engine() || !flutter_controller_->view()) {
+ return false;
+ }
+ RegisterPlugins(flutter_controller_->engine());
+ SetChildContent(flutter_controller_->view()->GetNativeWindow());
+
+ flutter_controller_->engine()->SetNextFrameCallback([&]() {
+ this->Show();
+ });
+
+ // Flutter can complete the first frame before the "show window" callback is
+ // registered. The following call ensures a frame is pending to ensure the
+ // window is shown. It is a no-op if the first frame hasn't completed yet.
+ flutter_controller_->ForceRedraw();
+
+ return true;
+}
+
+void FlutterWindow::OnDestroy() {
+ if (flutter_controller_) {
+ flutter_controller_ = nullptr;
+ }
+
+ Win32Window::OnDestroy();
+}
+
+LRESULT
+FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ // Give Flutter, including plugins, an opportunity to handle window messages.
+ if (flutter_controller_) {
+ std::optional result =
+ flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
+ lparam);
+ if (result) {
+ return *result;
+ }
+ }
+
+ switch (message) {
+ case WM_FONTCHANGE:
+ flutter_controller_->engine()->ReloadSystemFonts();
+ break;
+ }
+
+ return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
+}
diff --git a/bloc_flutter/windows/runner/flutter_window.h b/bloc_flutter/windows/runner/flutter_window.h
new file mode 100644
index 00000000..6da0652f
--- /dev/null
+++ b/bloc_flutter/windows/runner/flutter_window.h
@@ -0,0 +1,33 @@
+#ifndef RUNNER_FLUTTER_WINDOW_H_
+#define RUNNER_FLUTTER_WINDOW_H_
+
+#include
+#include
+
+#include
+
+#include "win32_window.h"
+
+// A window that does nothing but host a Flutter view.
+class FlutterWindow : public Win32Window {
+ public:
+ // Creates a new FlutterWindow hosting a Flutter view running |project|.
+ explicit FlutterWindow(const flutter::DartProject& project);
+ virtual ~FlutterWindow();
+
+ protected:
+ // Win32Window:
+ bool OnCreate() override;
+ void OnDestroy() override;
+ LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept override;
+
+ private:
+ // The project to run.
+ flutter::DartProject project_;
+
+ // The Flutter instance hosted by this window.
+ std::unique_ptr flutter_controller_;
+};
+
+#endif // RUNNER_FLUTTER_WINDOW_H_
diff --git a/bloc_flutter/windows/runner/main.cpp b/bloc_flutter/windows/runner/main.cpp
new file mode 100644
index 00000000..a26af59f
--- /dev/null
+++ b/bloc_flutter/windows/runner/main.cpp
@@ -0,0 +1,43 @@
+#include
+#include
+#include
+
+#include "flutter_window.h"
+#include "utils.h"
+
+int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
+ _In_ wchar_t *command_line, _In_ int show_command) {
+ // Attach to console when present (e.g., 'flutter run') or create a
+ // new console when running with a debugger.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+ CreateAndAttachConsole();
+ }
+
+ // Initialize COM, so that it is available for use in the library and/or
+ // plugins.
+ ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ flutter::DartProject project(L"data");
+
+ std::vector command_line_arguments =
+ GetCommandLineArguments();
+
+ project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
+
+ FlutterWindow window(project);
+ Win32Window::Point origin(10, 10);
+ Win32Window::Size size(1280, 720);
+ if (!window.Create(L"bloc_flutter_sample", origin, size)) {
+ return EXIT_FAILURE;
+ }
+ window.SetQuitOnClose(true);
+
+ ::MSG msg;
+ while (::GetMessage(&msg, nullptr, 0, 0)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+
+ ::CoUninitialize();
+ return EXIT_SUCCESS;
+}
diff --git a/bloc_flutter/windows/runner/resource.h b/bloc_flutter/windows/runner/resource.h
new file mode 100644
index 00000000..66a65d1e
--- /dev/null
+++ b/bloc_flutter/windows/runner/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Runner.rc
+//
+#define IDI_APP_ICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/bloc_flutter/windows/runner/resources/app_icon.ico b/bloc_flutter/windows/runner/resources/app_icon.ico
new file mode 100644
index 00000000..c04e20ca
Binary files /dev/null and b/bloc_flutter/windows/runner/resources/app_icon.ico differ
diff --git a/bloc_flutter/windows/runner/runner.exe.manifest b/bloc_flutter/windows/runner/runner.exe.manifest
new file mode 100644
index 00000000..153653e8
--- /dev/null
+++ b/bloc_flutter/windows/runner/runner.exe.manifest
@@ -0,0 +1,14 @@
+
+
+
+
+ PerMonitorV2
+
+
+
+
+
+
+
+
+
diff --git a/bloc_flutter/windows/runner/utils.cpp b/bloc_flutter/windows/runner/utils.cpp
new file mode 100644
index 00000000..3a0b4651
--- /dev/null
+++ b/bloc_flutter/windows/runner/utils.cpp
@@ -0,0 +1,65 @@
+#include "utils.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+void CreateAndAttachConsole() {
+ if (::AllocConsole()) {
+ FILE *unused;
+ if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+ _dup2(_fileno(stdout), 2);
+ }
+ std::ios::sync_with_stdio();
+ FlutterDesktopResyncOutputStreams();
+ }
+}
+
+std::vector GetCommandLineArguments() {
+ // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
+ int argc;
+ wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
+ if (argv == nullptr) {
+ return std::vector();
+ }
+
+ std::vector command_line_arguments;
+
+ // Skip the first argument as it's the binary name.
+ for (int i = 1; i < argc; i++) {
+ command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
+ }
+
+ ::LocalFree(argv);
+
+ return command_line_arguments;
+}
+
+std::string Utf8FromUtf16(const wchar_t* utf16_string) {
+ if (utf16_string == nullptr) {
+ return std::string();
+ }
+ unsigned int target_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
+ -1, nullptr, 0, nullptr, nullptr)
+ -1; // remove the trailing null character
+ int input_length = (int)wcslen(utf16_string);
+ std::string utf8_string;
+ if (target_length == 0 || target_length > utf8_string.max_size()) {
+ return utf8_string;
+ }
+ utf8_string.resize(target_length);
+ int converted_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
+ input_length, utf8_string.data(), target_length, nullptr, nullptr);
+ if (converted_length == 0) {
+ return std::string();
+ }
+ return utf8_string;
+}
diff --git a/bloc_flutter/windows/runner/utils.h b/bloc_flutter/windows/runner/utils.h
new file mode 100644
index 00000000..3879d547
--- /dev/null
+++ b/bloc_flutter/windows/runner/utils.h
@@ -0,0 +1,19 @@
+#ifndef RUNNER_UTILS_H_
+#define RUNNER_UTILS_H_
+
+#include
+#include
+
+// Creates a console for the process, and redirects stdout and stderr to
+// it for both the runner and the Flutter library.
+void CreateAndAttachConsole();
+
+// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
+// encoded in UTF-8. Returns an empty std::string on failure.
+std::string Utf8FromUtf16(const wchar_t* utf16_string);
+
+// Gets the command line arguments passed in as a std::vector,
+// encoded in UTF-8. Returns an empty std::vector on failure.
+std::vector GetCommandLineArguments();
+
+#endif // RUNNER_UTILS_H_
diff --git a/bloc_flutter/windows/runner/win32_window.cpp b/bloc_flutter/windows/runner/win32_window.cpp
new file mode 100644
index 00000000..60608d0f
--- /dev/null
+++ b/bloc_flutter/windows/runner/win32_window.cpp
@@ -0,0 +1,288 @@
+#include "win32_window.h"
+
+#include
+#include
+
+#include "resource.h"
+
+namespace {
+
+/// Window attribute that enables dark mode window decorations.
+///
+/// Redefined in case the developer's machine has a Windows SDK older than
+/// version 10.0.22000.0.
+/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
+#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
+#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
+#endif
+
+constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
+
+/// Registry key for app theme preference.
+///
+/// A value of 0 indicates apps should use dark mode. A non-zero or missing
+/// value indicates apps should use light mode.
+constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
+constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
+
+// The number of Win32Window objects that currently exist.
+static int g_active_window_count = 0;
+
+using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
+
+// Scale helper to convert logical scaler values to physical using passed in
+// scale factor
+int Scale(int source, double scale_factor) {
+ return static_cast(source * scale_factor);
+}
+
+// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
+// This API is only needed for PerMonitor V1 awareness mode.
+void EnableFullDpiSupportIfAvailable(HWND hwnd) {
+ HMODULE user32_module = LoadLibraryA("User32.dll");
+ if (!user32_module) {
+ return;
+ }
+ auto enable_non_client_dpi_scaling =
+ reinterpret_cast(
+ GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
+ if (enable_non_client_dpi_scaling != nullptr) {
+ enable_non_client_dpi_scaling(hwnd);
+ }
+ FreeLibrary(user32_module);
+}
+
+} // namespace
+
+// Manages the Win32Window's window class registration.
+class WindowClassRegistrar {
+ public:
+ ~WindowClassRegistrar() = default;
+
+ // Returns the singleton registrar instance.
+ static WindowClassRegistrar* GetInstance() {
+ if (!instance_) {
+ instance_ = new WindowClassRegistrar();
+ }
+ return instance_;
+ }
+
+ // Returns the name of the window class, registering the class if it hasn't
+ // previously been registered.
+ const wchar_t* GetWindowClass();
+
+ // Unregisters the window class. Should only be called if there are no
+ // instances of the window.
+ void UnregisterWindowClass();
+
+ private:
+ WindowClassRegistrar() = default;
+
+ static WindowClassRegistrar* instance_;
+
+ bool class_registered_ = false;
+};
+
+WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
+
+const wchar_t* WindowClassRegistrar::GetWindowClass() {
+ if (!class_registered_) {
+ WNDCLASS window_class{};
+ window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ window_class.lpszClassName = kWindowClassName;
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = GetModuleHandle(nullptr);
+ window_class.hIcon =
+ LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
+ window_class.hbrBackground = 0;
+ window_class.lpszMenuName = nullptr;
+ window_class.lpfnWndProc = Win32Window::WndProc;
+ RegisterClass(&window_class);
+ class_registered_ = true;
+ }
+ return kWindowClassName;
+}
+
+void WindowClassRegistrar::UnregisterWindowClass() {
+ UnregisterClass(kWindowClassName, nullptr);
+ class_registered_ = false;
+}
+
+Win32Window::Win32Window() {
+ ++g_active_window_count;
+}
+
+Win32Window::~Win32Window() {
+ --g_active_window_count;
+ Destroy();
+}
+
+bool Win32Window::Create(const std::wstring& title,
+ const Point& origin,
+ const Size& size) {
+ Destroy();
+
+ const wchar_t* window_class =
+ WindowClassRegistrar::GetInstance()->GetWindowClass();
+
+ const POINT target_point = {static_cast(origin.x),
+ static_cast(origin.y)};
+ HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
+ UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
+ double scale_factor = dpi / 96.0;
+
+ HWND window = CreateWindow(
+ window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
+ Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
+ Scale(size.width, scale_factor), Scale(size.height, scale_factor),
+ nullptr, nullptr, GetModuleHandle(nullptr), this);
+
+ if (!window) {
+ return false;
+ }
+
+ UpdateTheme(window);
+
+ return OnCreate();
+}
+
+bool Win32Window::Show() {
+ return ShowWindow(window_handle_, SW_SHOWNORMAL);
+}
+
+// static
+LRESULT CALLBACK Win32Window::WndProc(HWND const window,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ if (message == WM_NCCREATE) {
+ auto window_struct = reinterpret_cast(lparam);
+ SetWindowLongPtr(window, GWLP_USERDATA,
+ reinterpret_cast(window_struct->lpCreateParams));
+
+ auto that = static_cast(window_struct->lpCreateParams);
+ EnableFullDpiSupportIfAvailable(window);
+ that->window_handle_ = window;
+ } else if (Win32Window* that = GetThisFromHandle(window)) {
+ return that->MessageHandler(window, message, wparam, lparam);
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+LRESULT
+Win32Window::MessageHandler(HWND hwnd,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ switch (message) {
+ case WM_DESTROY:
+ window_handle_ = nullptr;
+ Destroy();
+ if (quit_on_close_) {
+ PostQuitMessage(0);
+ }
+ return 0;
+
+ case WM_DPICHANGED: {
+ auto newRectSize = reinterpret_cast(lparam);
+ LONG newWidth = newRectSize->right - newRectSize->left;
+ LONG newHeight = newRectSize->bottom - newRectSize->top;
+
+ SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
+ newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
+
+ return 0;
+ }
+ case WM_SIZE: {
+ RECT rect = GetClientArea();
+ if (child_content_ != nullptr) {
+ // Size and position the child window.
+ MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
+ rect.bottom - rect.top, TRUE);
+ }
+ return 0;
+ }
+
+ case WM_ACTIVATE:
+ if (child_content_ != nullptr) {
+ SetFocus(child_content_);
+ }
+ return 0;
+
+ case WM_DWMCOLORIZATIONCOLORCHANGED:
+ UpdateTheme(hwnd);
+ return 0;
+ }
+
+ return DefWindowProc(window_handle_, message, wparam, lparam);
+}
+
+void Win32Window::Destroy() {
+ OnDestroy();
+
+ if (window_handle_) {
+ DestroyWindow(window_handle_);
+ window_handle_ = nullptr;
+ }
+ if (g_active_window_count == 0) {
+ WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
+ }
+}
+
+Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
+ return reinterpret_cast(
+ GetWindowLongPtr(window, GWLP_USERDATA));
+}
+
+void Win32Window::SetChildContent(HWND content) {
+ child_content_ = content;
+ SetParent(content, window_handle_);
+ RECT frame = GetClientArea();
+
+ MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
+ frame.bottom - frame.top, true);
+
+ SetFocus(child_content_);
+}
+
+RECT Win32Window::GetClientArea() {
+ RECT frame;
+ GetClientRect(window_handle_, &frame);
+ return frame;
+}
+
+HWND Win32Window::GetHandle() {
+ return window_handle_;
+}
+
+void Win32Window::SetQuitOnClose(bool quit_on_close) {
+ quit_on_close_ = quit_on_close;
+}
+
+bool Win32Window::OnCreate() {
+ // No-op; provided for subclasses.
+ return true;
+}
+
+void Win32Window::OnDestroy() {
+ // No-op; provided for subclasses.
+}
+
+void Win32Window::UpdateTheme(HWND const window) {
+ DWORD light_mode;
+ DWORD light_mode_size = sizeof(light_mode);
+ LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
+ kGetPreferredBrightnessRegValue,
+ RRF_RT_REG_DWORD, nullptr, &light_mode,
+ &light_mode_size);
+
+ if (result == ERROR_SUCCESS) {
+ BOOL enable_dark_mode = light_mode == 0;
+ DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
+ &enable_dark_mode, sizeof(enable_dark_mode));
+ }
+}
diff --git a/bloc_flutter/windows/runner/win32_window.h b/bloc_flutter/windows/runner/win32_window.h
new file mode 100644
index 00000000..e901dde6
--- /dev/null
+++ b/bloc_flutter/windows/runner/win32_window.h
@@ -0,0 +1,102 @@
+#ifndef RUNNER_WIN32_WINDOW_H_
+#define RUNNER_WIN32_WINDOW_H_
+
+#include
+
+#include
+#include
+#include
+
+// A class abstraction for a high DPI-aware Win32 Window. Intended to be
+// inherited from by classes that wish to specialize with custom
+// rendering and input handling
+class Win32Window {
+ public:
+ struct Point {
+ unsigned int x;
+ unsigned int y;
+ Point(unsigned int x, unsigned int y) : x(x), y(y) {}
+ };
+
+ struct Size {
+ unsigned int width;
+ unsigned int height;
+ Size(unsigned int width, unsigned int height)
+ : width(width), height(height) {}
+ };
+
+ Win32Window();
+ virtual ~Win32Window();
+
+ // Creates a win32 window with |title| that is positioned and sized using
+ // |origin| and |size|. New windows are created on the default monitor. Window
+ // sizes are specified to the OS in physical pixels, hence to ensure a
+ // consistent size this function will scale the inputted width and height as
+ // as appropriate for the default monitor. The window is invisible until
+ // |Show| is called. Returns true if the window was created successfully.
+ bool Create(const std::wstring& title, const Point& origin, const Size& size);
+
+ // Show the current window. Returns true if the window was successfully shown.
+ bool Show();
+
+ // Release OS resources associated with window.
+ void Destroy();
+
+ // Inserts |content| into the window tree.
+ void SetChildContent(HWND content);
+
+ // Returns the backing Window handle to enable clients to set icon and other
+ // window properties. Returns nullptr if the window has been destroyed.
+ HWND GetHandle();
+
+ // If true, closing this window will quit the application.
+ void SetQuitOnClose(bool quit_on_close);
+
+ // Return a RECT representing the bounds of the current client area.
+ RECT GetClientArea();
+
+ protected:
+ // Processes and route salient window messages for mouse handling,
+ // size change and DPI. Delegates handling of these to member overloads that
+ // inheriting classes can handle.
+ virtual LRESULT MessageHandler(HWND window,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Called when CreateAndShow is called, allowing subclass window-related
+ // setup. Subclasses should return false if setup fails.
+ virtual bool OnCreate();
+
+ // Called when Destroy is called.
+ virtual void OnDestroy();
+
+ private:
+ friend class WindowClassRegistrar;
+
+ // OS callback called by message pump. Handles the WM_NCCREATE message which
+ // is passed when the non-client area is being created and enables automatic
+ // non-client DPI scaling so that the non-client area automatically
+ // responds to changes in DPI. All other messages are handled by
+ // MessageHandler.
+ static LRESULT CALLBACK WndProc(HWND const window,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Retrieves a class instance pointer for |window|
+ static Win32Window* GetThisFromHandle(HWND const window) noexcept;
+
+ // Update the window frame's theme to match the system theme.
+ static void UpdateTheme(HWND const window);
+
+ bool quit_on_close_ = false;
+
+ // window handle for top level window.
+ HWND window_handle_ = nullptr;
+
+ // window handle for hosted content.
+ HWND child_content_ = nullptr;
+};
+
+#endif // RUNNER_WIN32_WINDOW_H_
diff --git a/bloc_library/.metadata b/bloc_library/.metadata
index 1b5cec02..05a8ab44 100644
--- a/bloc_library/.metadata
+++ b/bloc_library/.metadata
@@ -4,7 +4,42 @@
# This file should be version controlled and should not be manually edited.
version:
- revision: 27321ebbad34b0a3fafe99fac037102196d655ff
- channel: stable
+ revision: "05db9689081f091050f01aed79f04dce0c750154"
+ channel: "stable"
project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: android
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: ios
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: linux
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: macos
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: web
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+ - platform: windows
+ create_revision: 05db9689081f091050f01aed79f04dce0c750154
+ base_revision: 05db9689081f091050f01aed79f04dce0c750154
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/bloc_library/analysis_options.yaml b/bloc_library/analysis_options.yaml
new file mode 100644
index 00000000..134f2137
--- /dev/null
+++ b/bloc_library/analysis_options.yaml
@@ -0,0 +1,34 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+analyzer:
+ language:
+ strict-casts: true
+ strict-inference: true
+ strict-raw-types: true
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at https://dart.dev/lints.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/bloc_library/android/app/build.gradle.kts b/bloc_library/android/app/build.gradle.kts
new file mode 100644
index 00000000..fd6e34c9
--- /dev/null
+++ b/bloc_library/android/app/build.gradle.kts
@@ -0,0 +1,44 @@
+plugins {
+ id("com.android.application")
+ id("kotlin-android")
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id("dev.flutter.flutter-gradle-plugin")
+}
+
+android {
+ namespace = "com.example.bloc_library"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_11.toString()
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId = "com.example.bloc_library"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = flutter.minSdkVersion
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ }
+}
+
+flutter {
+ source = "../.."
+}
diff --git a/bloc_library/android/app/src/main/res/drawable-v21/launch_background.xml b/bloc_library/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 00000000..f74085f3
--- /dev/null
+++ b/bloc_library/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/bloc_library/android/app/src/main/res/values-night/styles.xml b/bloc_library/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 00000000..06952be7
--- /dev/null
+++ b/bloc_library/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/bloc_library/android/build.gradle.kts b/bloc_library/android/build.gradle.kts
new file mode 100644
index 00000000..dbee657b
--- /dev/null
+++ b/bloc_library/android/build.gradle.kts
@@ -0,0 +1,24 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+val newBuildDir: Directory =
+ rootProject.layout.buildDirectory
+ .dir("../../build")
+ .get()
+rootProject.layout.buildDirectory.value(newBuildDir)
+
+subprojects {
+ val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
+ project.layout.buildDirectory.value(newSubprojectBuildDir)
+}
+subprojects {
+ project.evaluationDependsOn(":app")
+}
+
+tasks.register("clean") {
+ delete(rootProject.layout.buildDirectory)
+}
diff --git a/bloc_library/android/settings.gradle.kts b/bloc_library/android/settings.gradle.kts
new file mode 100644
index 00000000..fb605bc8
--- /dev/null
+++ b/bloc_library/android/settings.gradle.kts
@@ -0,0 +1,26 @@
+pluginManagement {
+ val flutterSdkPath =
+ run {
+ val properties = java.util.Properties()
+ file("local.properties").inputStream().use { properties.load(it) }
+ val flutterSdkPath = properties.getProperty("flutter.sdk")
+ require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
+ flutterSdkPath
+ }
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id("dev.flutter.flutter-plugin-loader") version "1.0.0"
+ id("com.android.application") version "8.9.1" apply false
+ id("org.jetbrains.kotlin.android") version "2.1.0" apply false
+}
+
+include(":app")
diff --git a/bloc_library/integration_test/app_test.dart b/bloc_library/integration_test/app_test.dart
new file mode 100644
index 00000000..6af7a098
--- /dev/null
+++ b/bloc_library/integration_test/app_test.dart
@@ -0,0 +1,19 @@
+import 'package:bloc_library/app.dart';
+import 'package:integration_tests/integration_tests.dart' as integration_tests;
+import 'package:shared_preferences/shared_preferences.dart';
+import 'package:todos_repository_local_storage/todos_repository_local_storage.dart';
+
+void main() {
+ integration_tests.run(
+ appBuilder: () async {
+ return TodosApp(
+ repository: LocalStorageRepository(
+ localStorage: KeyValueStorage(
+ 'bloc_library_test_${DateTime.now().toIso8601String()}',
+ await SharedPreferences.getInstance(),
+ ),
+ ),
+ );
+ },
+ );
+}
diff --git a/bloc_library/ios/Flutter/ephemeral/flutter_lldb_helper.py b/bloc_library/ios/Flutter/ephemeral/flutter_lldb_helper.py
new file mode 100644
index 00000000..a88caf99
--- /dev/null
+++ b/bloc_library/ios/Flutter/ephemeral/flutter_lldb_helper.py
@@ -0,0 +1,32 @@
+#
+# Generated file, do not edit.
+#
+
+import lldb
+
+def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
+ """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
+ base = frame.register["x0"].GetValueAsAddress()
+ page_len = frame.register["x1"].GetValueAsUnsigned()
+
+ # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
+ # first page to see if handled it correctly. This makes diagnosing
+ # misconfiguration (e.g. missing breakpoint) easier.
+ data = bytearray(page_len)
+ data[0:8] = b'IHELPED!'
+
+ error = lldb.SBError()
+ frame.GetThread().GetProcess().WriteMemory(base, data, error)
+ if not error.Success():
+ print(f'Failed to write into {base}[+{page_len}]', error)
+ return
+
+def __lldb_init_module(debugger: lldb.SBDebugger, _):
+ target = debugger.GetDummyTarget()
+ # Caveat: must use BreakpointCreateByRegEx here and not
+ # BreakpointCreateByName. For some reasons callback function does not
+ # get carried over from dummy target for the later.
+ bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
+ bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
+ bp.SetAutoContinue(True)
+ print("-- LLDB integration loaded --")
diff --git a/bloc_library/ios/Flutter/ephemeral/flutter_lldbinit b/bloc_library/ios/Flutter/ephemeral/flutter_lldbinit
new file mode 100644
index 00000000..e3ba6fbe
--- /dev/null
+++ b/bloc_library/ios/Flutter/ephemeral/flutter_lldbinit
@@ -0,0 +1,5 @@
+#
+# Generated file, do not edit.
+#
+
+command script import --relative-to-command-file flutter_lldb_helper.py
diff --git a/bloc_library/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/bloc_library/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/bloc_library/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/bloc_library/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/bloc_library/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/bloc_library/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/bloc_library/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/bloc_library/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/bloc_library/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/bloc_library/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/bloc_library/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/bloc_library/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/bloc_library/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/bloc_library/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 00000000..dc9ada47
Binary files /dev/null and b/bloc_library/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/bloc_library/ios/RunnerTests/RunnerTests.swift b/bloc_library/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 00000000..86a7c3b1
--- /dev/null
+++ b/bloc_library/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import Flutter
+import UIKit
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/bloc_library/lib/app.dart b/bloc_library/lib/app.dart
new file mode 100644
index 00000000..d8e2d1f9
--- /dev/null
+++ b/bloc_library/lib/app.dart
@@ -0,0 +1,63 @@
+import 'package:bloc_library/blocs/blocs.dart';
+import 'package:bloc_library/localization.dart';
+import 'package:bloc_library/models/models.dart';
+import 'package:bloc_library/screens/screens.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+import 'package:todos_repository_core/todos_repository_core.dart';
+
+class TodosApp extends StatelessWidget {
+ const TodosApp({super.key, required this.repository});
+
+ final TodosRepository repository;
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocProvider(
+ create: (context) {
+ return TodosBloc(todosRepository: repository)..add(LoadTodos());
+ },
+ child: MaterialApp(
+ onGenerateTitle: (context) =>
+ FlutterBlocLocalizations.of(context).appTitle,
+ theme: ArchSampleTheme.lightTheme,
+ darkTheme: ArchSampleTheme.darkTheme,
+ localizationsDelegates: [
+ ArchSampleLocalizationsDelegate(),
+ FlutterBlocLocalizationsDelegate(),
+ ],
+ routes: {
+ ArchSampleRoutes.home: (context) {
+ return MultiBlocProvider(
+ providers: [
+ BlocProvider(create: (context) => TabBloc()),
+ BlocProvider(
+ create: (context) => FilteredTodosBloc(
+ todosBloc: BlocProvider.of(context),
+ ),
+ ),
+ BlocProvider(
+ create: (context) =>
+ StatsBloc(todosBloc: BlocProvider.of(context)),
+ ),
+ ],
+ child: HomeScreen(),
+ );
+ },
+ ArchSampleRoutes.addTodo: (context) {
+ return AddEditScreen(
+ key: ArchSampleKeys.addTodoScreen,
+ onSave: (task, note) {
+ BlocProvider.of(
+ context,
+ ).add(AddTodo(Todo(task, note: note)));
+ },
+ isEditing: false,
+ );
+ },
+ },
+ ),
+ );
+ }
+}
diff --git a/bloc_library/lib/bloc_library_keys.dart b/bloc_library/lib/bloc_library_keys.dart
index 79cabe6a..81bab004 100644
--- a/bloc_library/lib/bloc_library_keys.dart
+++ b/bloc_library/lib/bloc_library_keys.dart
@@ -1,16 +1,15 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:flutter/widgets.dart';
class BlocLibraryKeys {
- static final extraActionsPopupMenuButton =
- const Key('__extraActionsPopupMenuButton__');
- static final extraActionsEmptyContainer =
- const Key('__extraActionsEmptyContainer__');
- static final filteredTodosEmptyContainer =
- const Key('__filteredTodosEmptyContainer__');
+ static final extraActionsPopupMenuButton = const Key(
+ '__extraActionsPopupMenuButton__',
+ );
+ static final extraActionsEmptyContainer = const Key(
+ '__extraActionsEmptyContainer__',
+ );
+ static final filteredTodosEmptyContainer = const Key(
+ '__filteredTodosEmptyContainer__',
+ );
static final statsLoadingIndicator = const Key('__statsLoadingIndicator__');
static final emptyStatsContainer = const Key('__emptyStatsContainer__');
static final emptyDetailsContainer = const Key('__emptyDetailsContainer__');
diff --git a/bloc_library/lib/blocs/blocs.dart b/bloc_library/lib/blocs/blocs.dart
index b4e836a2..c749033b 100644
--- a/bloc_library/lib/blocs/blocs.dart
+++ b/bloc_library/lib/blocs/blocs.dart
@@ -1,9 +1,5 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
export './filtered_todos/filtered_todos.dart';
export './stats/stats.dart';
export './tab/tab.dart';
export './todos/todos.dart';
-export './simple_bloc_delegate.dart';
+export 'simple_bloc_observer.dart';
diff --git a/bloc_library/lib/blocs/filtered_todos/filtered_todos.dart b/bloc_library/lib/blocs/filtered_todos/filtered_todos.dart
index 2dab7174..f15a9590 100644
--- a/bloc_library/lib/blocs/filtered_todos/filtered_todos.dart
+++ b/bloc_library/lib/blocs/filtered_todos/filtered_todos.dart
@@ -1,7 +1,3 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
export './filtered_todos_bloc.dart';
export './filtered_todos_event.dart';
export './filtered_todos_state.dart';
diff --git a/bloc_library/lib/blocs/filtered_todos/filtered_todos_bloc.dart b/bloc_library/lib/blocs/filtered_todos/filtered_todos_bloc.dart
index 19f268c8..7351cd60 100644
--- a/bloc_library/lib/blocs/filtered_todos/filtered_todos_bloc.dart
+++ b/bloc_library/lib/blocs/filtered_todos/filtered_todos_bloc.dart
@@ -1,71 +1,60 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'dart:async';
+
import 'package:bloc/bloc.dart';
-import 'package:meta/meta.dart';
import 'package:bloc_library/blocs/filtered_todos/filtered_todos.dart';
import 'package:bloc_library/blocs/todos/todos.dart';
import 'package:bloc_library/models/models.dart';
class FilteredTodosBloc extends Bloc {
final TodosBloc todosBloc;
- StreamSubscription todosSubscription;
+ late final StreamSubscription todosSubscription;
- FilteredTodosBloc({@required this.todosBloc}) {
- todosSubscription = todosBloc.listen((state) {
+ FilteredTodosBloc({required this.todosBloc})
+ : super(
+ todosBloc.state is TodosLoaded
+ ? FilteredTodosLoaded(
+ (todosBloc.state as TodosLoaded).todos,
+ VisibilityFilter.all,
+ )
+ : FilteredTodosLoading(),
+ ) {
+ todosSubscription = todosBloc.stream.listen((state) {
if (state is TodosLoaded) {
add(UpdateTodos(state.todos));
}
});
- }
-
- @override
- FilteredTodosState get initialState {
- return todosBloc.state is TodosLoaded
- ? FilteredTodosLoaded(
- (todosBloc.state as TodosLoaded).todos,
- VisibilityFilter.all,
- )
- : FilteredTodosLoading();
- }
- @override
- Stream mapEventToState(FilteredTodosEvent event) async* {
- if (event is UpdateFilter) {
- yield* _mapUpdateFilterToState(event);
- } else if (event is UpdateTodos) {
- yield* _mapTodosUpdatedToState(event);
- }
+ on(_onUpdateFilter);
+ on(_onUpdateTodos);
}
- Stream _mapUpdateFilterToState(
- UpdateFilter event,
- ) async* {
+ void _onUpdateFilter(UpdateFilter event, Emitter emit) {
if (todosBloc.state is TodosLoaded) {
- yield FilteredTodosLoaded(
- _mapTodosToFilteredTodos(
- (todosBloc.state as TodosLoaded).todos,
+ emit(
+ FilteredTodosLoaded(
+ _mapTodosToFilteredTodos(
+ (todosBloc.state as TodosLoaded).todos,
+ event.filter,
+ ),
event.filter,
),
- event.filter,
);
}
}
- Stream _mapTodosUpdatedToState(
- UpdateTodos event,
- ) async* {
+ void _onUpdateTodos(UpdateTodos event, Emitter emit) {
final visibilityFilter = state is FilteredTodosLoaded
? (state as FilteredTodosLoaded).activeFilter
: VisibilityFilter.all;
- yield FilteredTodosLoaded(
- _mapTodosToFilteredTodos(
- (todosBloc.state as TodosLoaded).todos,
+
+ emit(
+ FilteredTodosLoaded(
+ _mapTodosToFilteredTodos(
+ (todosBloc.state as TodosLoaded).todos,
+ visibilityFilter,
+ ),
visibilityFilter,
),
- visibilityFilter,
);
}
@@ -86,7 +75,7 @@ class FilteredTodosBloc extends Bloc {
@override
Future close() {
- todosSubscription?.cancel();
+ todosSubscription.cancel();
return super.close();
}
}
diff --git a/bloc_library/lib/blocs/filtered_todos/filtered_todos_event.dart b/bloc_library/lib/blocs/filtered_todos/filtered_todos_event.dart
index 24ff55d8..7a07f880 100644
--- a/bloc_library/lib/blocs/filtered_todos/filtered_todos_event.dart
+++ b/bloc_library/lib/blocs/filtered_todos/filtered_todos_event.dart
@@ -1,7 +1,3 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:equatable/equatable.dart';
import 'package:bloc_library/models/models.dart';
diff --git a/bloc_library/lib/blocs/filtered_todos/filtered_todos_state.dart b/bloc_library/lib/blocs/filtered_todos/filtered_todos_state.dart
index 400d6172..89ec5158 100644
--- a/bloc_library/lib/blocs/filtered_todos/filtered_todos_state.dart
+++ b/bloc_library/lib/blocs/filtered_todos/filtered_todos_state.dart
@@ -1,7 +1,3 @@
-// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
-// Use of this source code is governed by the MIT license that can be found
-// in the LICENSE file.
-
import 'package:equatable/equatable.dart';
import 'package:bloc_library/models/models.dart';
@@ -18,10 +14,7 @@ class FilteredTodosLoaded extends FilteredTodosState {
final List filteredTodos;
final VisibilityFilter activeFilter;
- const FilteredTodosLoaded(
- this.filteredTodos,
- this.activeFilter,
- );
+ const FilteredTodosLoaded(this.filteredTodos, this.activeFilter);
@override
List