From b3cf006e1543b931a792892dbe0a28541f38752f Mon Sep 17 00:00:00 2001 From: Shady Boukhary Date: Tue, 2 Jul 2024 19:56:40 -0400 Subject: [PATCH 01/10] Dart 3 support --- analysis_options.yaml | 1 - example/.metadata | 24 +- example/analysis_options.yaml | 28 ++ example/android/.gitignore | 13 + example/android/app/build.gradle | 62 ++--- .../android/app/src/debug/AndroidManifest.xml | 6 +- .../android/app/src/main/AndroidManifest.xml | 44 +++- .../com/example/example/MainActivity.java | 5 - .../com/example/example/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../app/src/main/res/values-night/styles.xml | 18 ++ .../app/src/main/res/values/styles.xml | 14 +- .../app/src/profile/AndroidManifest.xml | 6 +- example/android/build.gradle | 20 +- example/android/gradle.properties | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 3 +- example/android/settings.gradle | 30 ++- .../ios/Flutter/flutter_export_environment.sh | 6 +- .../src/app/pages/home/home_controller.dart | 4 +- .../src/app/pages/home/home_presenter.dart | 21 +- example/lib/src/app/pages/home/home_view.dart | 20 +- example/lib/src/app/widgets/button.dart | 2 +- .../repositories/data_users_repository.dart | 3 +- .../src/domain/usecases/get_user_usecase.dart | 6 +- example/pubspec.lock | 247 ++++++++++++------ example/pubspec.yaml | 4 +- example/test/src/app/widgets/button_test.dart | 8 +- .../usecases/get_user_usecase_test.dart | 6 +- lib/src/controller.dart | 33 ++- lib/src/view.dart | 2 +- pubspec.lock | 146 +++++++---- pubspec.yaml | 6 +- test/flutter_clean_architecture_test.dart | 12 + 33 files changed, 543 insertions(+), 278 deletions(-) create mode 100644 example/analysis_options.yaml create mode 100644 example/android/.gitignore delete mode 100644 example/android/app/src/main/java/com/example/example/MainActivity.java create mode 100644 example/android/app/src/main/kotlin/com/example/example/MainActivity.kt create mode 100644 example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 example/android/app/src/main/res/values-night/styles.xml create mode 100644 test/flutter_clean_architecture_test.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index 105a4cf..8a17426 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,7 +4,6 @@ linter: rules: library_names: true avoid_empty_else: true - unecessary_const: true unawaited_futures: false avoid_print: false use_key_in_widget_constructors: false diff --git a/example/.metadata b/example/.metadata index 07763f7..8973d6c 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,27 @@ # This file should be version controlled and should not be manually edited. version: - revision: 8661d8aecd626f7f57ccbcb735553edc05a2e713 - channel: stable + revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" + channel: "stable" project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: android + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# 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 + +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/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index bd3cfe7..2a2d082 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,64 +1,58 @@ +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" +} + def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') +def localPropertiesFile = rootProject.file("local.properties") if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> + 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') +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") if (flutterVersionCode == null) { - flutterVersionCode = '1' + flutterVersionCode = "1" } -def flutterVersionName = localProperties.getProperty('flutter.versionName') +def flutterVersionName = localProperties.getProperty("flutter.versionName") if (flutterVersionName == null) { - flutterVersionName = '1.0' + flutterVersionName = "1.0" } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { - compileSdkVersion 31 + namespace = "com.example.example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion - lintOptions { - disable 'InvalidPackage' + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.example" - minSdkVersion 16 - targetSdkVersion 31 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + applicationId = "com.example.example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig = signingConfigs.debug } } } flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.4.1' - - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + source = "../.." } diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index c208884..399f698 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,6 +1,6 @@ - - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index adbb781..74a78b9 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,29 +1,45 @@ - - - + - + android:windowSoftInputMode="adjustResize"> + + + + + + + + + + + diff --git a/example/android/app/src/main/java/com/example/example/MainActivity.java b/example/android/app/src/main/java/com/example/example/MainActivity.java deleted file mode 100644 index b8e66b0..0000000 --- a/example/android/app/src/main/java/com/example/example/MainActivity.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.example.example; - -import io.flutter.embedding.android.FlutterActivity; - -public class MainActivity extends FlutterActivity {} diff --git a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt new file mode 100644 index 0000000..70f8f08 --- /dev/null +++ b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 00fa441..cb1ef88 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,8 +1,18 @@ - + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml index c208884..399f698 100644 --- a/example/android/app/src/profile/AndroidManifest.xml +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -1,6 +1,6 @@ - - diff --git a/example/android/build.gradle b/example/android/build.gradle index 31c86d5..d2ffbff 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,17 +1,3 @@ -buildscript { - ext.kotlin_version = '1.6.10' - - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.0.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() @@ -19,14 +5,14 @@ allprojects { } } -rootProject.buildDir = '../build' +rootProject.buildDir = "../build" subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { - project.evaluationDependsOn(':app') + project.evaluationDependsOn(":app") } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index d12b9a8..3b5b324 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index b8793d3..e1ca574 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 5a2f14f..536165d 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,15 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false } + +include ":app" diff --git a/example/ios/Flutter/flutter_export_environment.sh b/example/ios/Flutter/flutter_export_environment.sh index df4161f..0021ddf 100755 --- a/example/ios/Flutter/flutter_export_environment.sh +++ b/example/ios/Flutter/flutter_export_environment.sh @@ -1,9 +1,9 @@ #!/bin/sh # This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=/Users/chrisianfiel/Projects/flutter" -export "FLUTTER_APPLICATION_PATH=/Users/chrisianfiel/Projects/flutter_clean_architecture/example" +export "FLUTTER_ROOT=C:\src\flutter" +export "FLUTTER_APPLICATION_PATH=C:\Users\sb199\flutter_clean\flutter_clean_architecture\example" export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=lib/main.dart" +export "FLUTTER_TARGET=lib\main.dart" export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_NAME=1.0.0" export "FLUTTER_BUILD_NUMBER=1" diff --git a/example/lib/src/app/pages/home/home_controller.dart b/example/lib/src/app/pages/home/home_controller.dart index a52476d..19d8533 100644 --- a/example/lib/src/app/pages/home/home_controller.dart +++ b/example/lib/src/app/pages/home/home_controller.dart @@ -5,9 +5,9 @@ import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; class HomeController extends Controller { int _counter; - User _user; + User? _user; int get counter => _counter; - User get user => _user; // data used by the View + User? get user => _user; // data used by the View final HomePresenter homePresenter; // Presenter should always be initialized this way HomeController(usersRepo) diff --git a/example/lib/src/app/pages/home/home_presenter.dart b/example/lib/src/app/pages/home/home_presenter.dart index 450e6b0..921d9e2 100644 --- a/example/lib/src/app/pages/home/home_presenter.dart +++ b/example/lib/src/app/pages/home/home_presenter.dart @@ -1,10 +1,10 @@ import '../../../domain/usecases/get_user_usecase.dart'; -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' as clean; -class HomePresenter extends Presenter { - Function getUserOnNext; - Function getUserOnComplete; - Function getUserOnError; +class HomePresenter extends clean.Presenter { + Function? getUserOnNext; + Function? getUserOnComplete; + Function? getUserOnError; final GetUserUseCase getUserUseCase; HomePresenter(usersRepo) : getUserUseCase = GetUserUseCase(usersRepo); @@ -21,24 +21,21 @@ class HomePresenter extends Presenter { } } -class _GetUserUseCaseObserver extends Observer { +class _GetUserUseCaseObserver extends clean.Observer { final HomePresenter presenter; _GetUserUseCaseObserver(this.presenter); @override void onComplete() { - assert(presenter.getUserOnComplete != null); - presenter.getUserOnComplete(); + presenter.getUserOnComplete?.call(); } @override void onError(e) { - assert(presenter.getUserOnError != null); - presenter.getUserOnError(e); + presenter.getUserOnError?.call(e); } @override void onNext(response) { - assert(presenter.getUserOnNext != null); - presenter.getUserOnNext(response.user); + presenter.getUserOnNext?.call(response?.user); } } diff --git a/example/lib/src/app/pages/home/home_view.dart b/example/lib/src/app/pages/home/home_view.dart index 6f5f53a..0e2916a 100644 --- a/example/lib/src/app/pages/home/home_view.dart +++ b/example/lib/src/app/pages/home/home_view.dart @@ -1,10 +1,10 @@ import './home_controller.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' as clean; import '../../../data/repositories/data_users_repository.dart'; -class HomePage extends View { - const HomePage({Key key, this.title}) : super(key: key); +class HomePage extends clean.View { + const HomePage({Key? key, this.title = ""}) : super(key: key); final String title; @@ -14,7 +14,7 @@ class HomePage extends View { HomePageState(); } -class HomePageState extends ViewState { +class HomePageState extends clean.ViewState { HomePageState() : super(HomeController(DataUsersRepository())); @override @@ -30,7 +30,7 @@ class HomePageState extends ViewState { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - ControlledWidgetBuilder( + clean.ControlledWidgetBuilder( builder: (context, controller) { return Text( 'Button pressed ${controller.counter} times.', @@ -40,15 +40,15 @@ class HomePageState extends ViewState { const Text( 'The current user is', ), - ControlledWidgetBuilder( + clean.ControlledWidgetBuilder( builder: (context, controller) { return Text( controller.user == null ? '' : '${controller.user}', - style: Theme.of(context).textTheme.headline4, + style: Theme.of(context).textTheme.headlineMedium, ); }, ), - ControlledWidgetBuilder( + clean.ControlledWidgetBuilder( builder: (context, controller) { return ElevatedButton( onPressed: controller.getUser, @@ -59,7 +59,7 @@ class HomePageState extends ViewState { ); }, ), - ControlledWidgetBuilder( + clean.ControlledWidgetBuilder( builder: (context, controller) { return ElevatedButton( onPressed: controller.getUserwithError, @@ -74,7 +74,7 @@ class HomePageState extends ViewState { ), ), ), - floatingActionButton: ControlledWidgetBuilder( + floatingActionButton: clean.ControlledWidgetBuilder( builder: (context, controller) { return FloatingActionButton( onPressed: () => controller.buttonPressed(), diff --git a/example/lib/src/app/widgets/button.dart b/example/lib/src/app/widgets/button.dart index 418e90c..e9d26b7 100644 --- a/example/lib/src/app/widgets/button.dart +++ b/example/lib/src/app/widgets/button.dart @@ -4,7 +4,7 @@ import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; class HomePageButton extends StatelessWidget { final String text; - const HomePageButton({@required this.text}); + const HomePageButton({required this.text}); @override Widget build(BuildContext context) { diff --git a/example/lib/src/data/repositories/data_users_repository.dart b/example/lib/src/data/repositories/data_users_repository.dart index 5f49432..90325c5 100644 --- a/example/lib/src/data/repositories/data_users_repository.dart +++ b/example/lib/src/data/repositories/data_users_repository.dart @@ -5,8 +5,7 @@ class DataUsersRepository extends UsersRepository { List users; // sigleton static final DataUsersRepository _instance = DataUsersRepository._internal(); - DataUsersRepository._internal() { - users = []; + DataUsersRepository._internal() : users = [] { users.addAll([ User('test-uid', 'John Smith', 18), User('test-uid2', 'John Doe', 22) diff --git a/example/lib/src/domain/usecases/get_user_usecase.dart b/example/lib/src/domain/usecases/get_user_usecase.dart index 4dfe7b2..b190032 100644 --- a/example/lib/src/domain/usecases/get_user_usecase.dart +++ b/example/lib/src/domain/usecases/get_user_usecase.dart @@ -10,12 +10,12 @@ class GetUserUseCase GetUserUseCase(this.usersRepository); @override - Future> buildUseCaseStream( - GetUserUseCaseParams params) async { + Future> buildUseCaseStream( + GetUserUseCaseParams? params) async { final controller = StreamController(); try { // get user - final user = await usersRepository.getUser(params.uid); + final user = await usersRepository.getUser(params!.uid); // Adding it triggers the .onNext() in the `Observer` // It is usually better to wrap the reponse inside a respose object. controller.add(GetUserUseCaseResponse(user)); diff --git a/example/pubspec.lock b/example/pubspec.lock index e69b809..da20998 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,135 +5,154 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - url: "https://pub.dartlang.org" + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.dev" source: hosted - version: "50.0.0" + version: "67.0.0" analyzer: dependency: transitive description: name: analyzer - url: "https://pub.dartlang.org" + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "6.4.1" args: dependency: transitive description: name: args - url: "https://pub.dartlang.org" + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.5.0" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" build: dependency: transitive description: name: build - url: "https://pub.dartlang.org" + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.1" built_collection: dependency: transitive description: name: built_collection - url: "https://pub.dartlang.org" + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" source: hosted version: "5.1.1" built_value: dependency: transitive description: name: built_value - url: "https://pub.dartlang.org" + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + url: "https://pub.dev" source: hosted - version: "8.4.4" + version: "8.9.2" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted version: "1.1.1" code_builder: dependency: transitive description: name: code_builder - url: "https://pub.dartlang.org" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.10.0" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.18.0" convert: dependency: transitive description: name: convert - url: "https://pub.dartlang.org" + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" source: hosted version: "3.1.1" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "1.0.8" dart_style: dependency: transitive description: name: dart_style - url: "https://pub.dartlang.org" + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + url: "https://pub.dev" source: hosted - version: "2.2.5" + version: "2.3.6" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted version: "1.3.1" file: dependency: transitive description: name: file - url: "https://pub.dartlang.org" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" fixnum: dependency: transitive description: name: fixnum - url: "https://pub.dartlang.org" + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -145,14 +164,15 @@ packages: path: ".." relative: true source: path - version: "5.0.3" + version: "6.0.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -162,98 +182,136 @@ packages: dependency: transitive description: name: glob - url: "https://pub.dartlang.org" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.1" logging: dependency: transitive description: name: logging - url: "https://pub.dartlang.org" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.2.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" source: hosted - version: "0.12.12" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.8.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.12.0" mockito: dependency: "direct main" description: name: mockito - url: "https://pub.dartlang.org" + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" + url: "https://pub.dev" source: hosted - version: "5.3.2" + version: "5.4.4" nested: dependency: transitive description: name: nested - url: "https://pub.dartlang.org" + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" source: hosted version: "1.0.0" package_config: dependency: transitive description: name: package_config - url: "https://pub.dartlang.org" + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" source: hosted version: "2.1.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.9.0" provider: dependency: transitive description: name: provider - url: "https://pub.dartlang.org" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "6.1.2" pub_semver: dependency: transitive description: name: pub_semver - url: "https://pub.dartlang.org" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" responsive_builder: dependency: transitive description: name: responsive_builder - url: "https://pub.dartlang.org" + sha256: f01bc341c73b6db7bd6319e22d2c160f28f924399ae46e6699ecc8160ba2765c + url: "https://pub.dev" source: hosted - version: "0.4.1" + version: "0.4.3" rxdart: dependency: transitive description: name: rxdart - url: "https://pub.dartlang.org" + sha256: "2ef8b4e91cb3b55d155e0e34eeae0ac7107974e451495c955ac04ddee8cc21fd" + url: "https://pub.dev" source: hosted version: "0.26.0" sky_engine: @@ -265,79 +323,98 @@ packages: dependency: transitive description: name: source_gen - url: "https://pub.dartlang.org" + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" source: hosted - version: "1.2.7" + version: "1.5.0" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" source: hosted - version: "0.4.12" + version: "0.7.0" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" + source: hosted + version: "14.2.1" watcher: dependency: transitive description: name: watcher - url: "https://pub.dartlang.org" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" yaml: dependency: transitive description: name: yaml - url: "https://pub.dartlang.org" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" sdks: - dart: ">=2.18.0 <3.0.0" - flutter: ">=1.16.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index ba3b6f9..96f20fc 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -16,7 +16,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: @@ -24,7 +24,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 + cupertino_icons: ^1.0.8 flutter_clean_architecture: path: ../ mockito: ^5.3.2 diff --git a/example/test/src/app/widgets/button_test.dart b/example/test/src/app/widgets/button_test.dart index a26c397..1515540 100644 --- a/example/test/src/app/widgets/button_test.dart +++ b/example/test/src/app/widgets/button_test.dart @@ -2,7 +2,7 @@ import 'package:example/src/app/pages/home/home_controller.dart'; import 'package:example/src/app/widgets/button.dart'; import 'package:example/src/data/repositories/data_users_repository.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' as clean; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -25,16 +25,16 @@ void main() { }); } -class TestPage extends View { +class TestPage extends clean.View { const TestPage({ - Key key, + Key? key, }) : super(key: key); @override TestPageState createState() => TestPageState(); } -class TestPageState extends ViewState { +class TestPageState extends clean.ViewState { TestPageState() : super(HomeController(DataUsersRepository())); @override diff --git a/example/test/src/domain/usecases/get_user_usecase_test.dart b/example/test/src/domain/usecases/get_user_usecase_test.dart index ab01248..c0ea503 100644 --- a/example/test/src/domain/usecases/get_user_usecase_test.dart +++ b/example/test/src/domain/usecases/get_user_usecase_test.dart @@ -14,7 +14,7 @@ void main() { getUserUseCase = GetUserUseCase(MockGetUser()); observer = _Observer(); getUserUseCase.execute(observer, GetUserUseCaseParams('1000-2000-5600')); - while (!observer.status['progress'].contains('done')) { + while (!observer.status['progress']!.contains('done')) { await Future.delayed(const Duration(seconds: 1)); } expect(observer.status['result'], 'success'); @@ -37,7 +37,7 @@ void main() { getUserUseCase = GetUserUseCase(MockGetUser()); observer = _Observer(); getUserUseCase.execute(observer, GetUserUseCaseParams('22222')); - while (!observer.status['progress'].contains('done')) { + while (!observer.status['progress']!.contains('done')) { await Future.delayed(const Duration(seconds: 1)); } expect(observer.status['result'], 'failed'); @@ -74,7 +74,7 @@ class MockGetUser extends Mock implements UsersRepository { @override Future getUser(String uid) async { - User testGetUser; + User? testGetUser; for (var user in users) { if (user.uid == uid) { testGetUser = user; diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 7d894c6..8ba8492 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' as clean; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; import 'package:provider/provider.dart'; /// A Clean Architecture [Controller]. Should be aggregated within a `ViewState` or -/// a `View`. However, it is preferable to be contained inside the `View` for readability +/// a `clean.View`. However, it is preferable to be contained inside the `clean.View` for readability /// and maintainability. /// -/// The [Controller] handles the events triggered by the `View`. For example, it handles +/// The [Controller] handles the events triggered by the `clean.View`. For example, it handles /// the click events of buttons, lifecycle, data-sourcing, etc... /// /// The [Controller] is also route-aware. However, in order to use it, @@ -57,7 +57,7 @@ import 'package:provider/provider.dart'; /// return MaterialApp( /// title: 'Flutter Demo', /// home: Scaffold( -/// key: globalKey, // using the built-in global key of the `View` for the scaffold or any other +/// key: globalKey, // using the built-in global key of the `clean.View` for the scaffold or any other /// // widget provides the controller with a way to access them via getContext(), getState(), getStateKey() /// body: Column( /// children: [ @@ -103,6 +103,9 @@ abstract class Controller case AppLifecycleState.detached: onDetached(); break; + case AppLifecycleState.hidden: + onHidden(); + break; } } } @@ -115,7 +118,7 @@ abstract class Controller } } - /// Unmounts the [Controller] from the `View`. Called by the `View` automatically. + /// Unmounts the [Controller] from the `clean.View`. Called by the `clean.View` automatically. /// Any cleaning, disposing should go in here. /// /// To perform correct actions that depends on latest [BuildContext] used on view before dispose, you must @@ -165,12 +168,12 @@ abstract class Controller } /// Initializes optional [Controller] variables that can be used for _refreshing and error displaying. - /// This method is called automatically by the mounted `View`. Do not call. + /// This method is called automatically by the mounted `clean.View`. Do not call. void initController(GlobalKey> key) { _globalKey = key; } - /// Retrieves the [BuildContext] associated with the `View`. Will throw an error if initController() was not called prior. + /// Retrieves the [BuildContext] associated with the `clean.View`. Will throw an error if initController() was not called prior. @protected BuildContext getContext() { assert(_globalKey.currentContext != null, @@ -259,6 +262,22 @@ abstract class Controller @visibleForOverriding void onDetached() {} + /// Called before the application is detached. + /// A callback that is called when the application is hidden. + /// On mobile platforms, this is usually just before the application is replaced by another application in the foreground. + /// On desktop platforms, this is just before the application is hidden by being minimized or otherwise hiding all views of the application. + /// On the web, this is just before a window (or tab) is hidden. + + /// + /// ```dart + /// class MyController extends Controller { + /// @override + /// void onHidden() => print('App is about to be hidden.'); + /// } + /// ``` + @visibleForOverriding + void onHidden() {} + /// Called before the view is deactivated. /// When the view is in this context, it means that the view is about to be extracted from the widget tree, but it may be /// added again. Quoting the view `deactivate` docs from `https://api.flutter.dev/flutter/widgets/State/deactivate.html`: diff --git a/lib/src/view.dart b/lib/src/view.dart index c07bb6f..766f0d2 100644 --- a/lib/src/view.dart +++ b/lib/src/view.dart @@ -139,7 +139,7 @@ abstract class ViewState ViewState(this._controller) { _controller.initController(globalKey); - WidgetsBinding.instance.addObserver(_controller); + WidgetsBinding?.instance!.addObserver(_controller); _logger = Logger('$runtimeType'); } diff --git a/pubspec.lock b/pubspec.lock index 9de632d..96cd1e4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,42 +5,48 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.18.0" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted version: "1.3.1" flutter: @@ -52,82 +58,117 @@ packages: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.3" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.1" logging: dependency: "direct main" description: name: logging - url: "https://pub.dartlang.org" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.2.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" source: hosted - version: "0.12.12" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.8.0" meta: dependency: "direct main" description: name: meta - url: "https://pub.dartlang.org" + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.12.0" nested: dependency: transitive description: name: nested - url: "https://pub.dartlang.org" + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" source: hosted version: "1.0.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.9.0" provider: dependency: "direct main" description: name: provider - url: "https://pub.dartlang.org" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "6.1.2" responsive_builder: dependency: "direct main" description: name: responsive_builder - url: "https://pub.dartlang.org" + sha256: f01bc341c73b6db7bd6319e22d2c160f28f924399ae46e6699ecc8160ba2765c + url: "https://pub.dev" source: hosted - version: "0.4.1" + version: "0.4.3" rxdart: dependency: "direct main" description: name: rxdart - url: "https://pub.dartlang.org" + sha256: "2ef8b4e91cb3b55d155e0e34eeae0ac7107974e451495c955ac04ddee8cc21fd" + url: "https://pub.dev" source: hosted version: "0.26.0" sky_engine: @@ -139,51 +180,66 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" source: hosted - version: "0.4.12" + version: "0.7.0" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" + source: hosted + version: "14.2.1" sdks: - dart: ">=2.17.0-206.0.dev <3.0.0" - flutter: ">=1.16.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index 86d4268..7e054c9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,17 +1,17 @@ name: flutter_clean_architecture description: A Flutter package that implements the Clean Architecture by Uncle Bob in Flutter. It provides Views, Controllers, Presenters, Observers, and UseCases. -version: 5.0.3 +version: 6.0.0 homepage: https://github.com/ShadyBoukhary/flutter_clean_architecture environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: sdk: flutter logging: ^1.0.0 meta: ^1.3.0 - provider: ^6.0.1 + provider: ^6.1.2 responsive_builder: ^0.4.1 rxdart: ^0.26.0 diff --git a/test/flutter_clean_architecture_test.dart b/test/flutter_clean_architecture_test.dart new file mode 100644 index 0000000..4fa7c3a --- /dev/null +++ b/test/flutter_clean_architecture_test.dart @@ -0,0 +1,12 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; + +void main() { + test('adds one to input values', () { + final calculator = Calculator(); + expect(calculator.addOne(2), 3); + expect(calculator.addOne(-7), -6); + expect(calculator.addOne(0), 1); + }); +} From 325fd2a83ff9edcd139714c098dfbaabc8ef88a8 Mon Sep 17 00:00:00 2001 From: Shady Boukhary Date: Tue, 2 Jul 2024 19:57:55 -0400 Subject: [PATCH 02/10] Update changelog and readme --- CHANGELOG.md | 3 +++ README.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08fbcf2..7e973ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## [6.0.0] - Tuesday, July 2nd, 2024 +- Migrate to Dart 3 + ## [5.0.4] - Saturday, March 17th, 2023 - Add 100% test coverage to the example diff --git a/README.md b/README.md index 1f03b0b..2fe0ab0 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Add this to your package's pubspec.yaml file: ```yaml dependencies: - flutter_clean_architecture: ^5.0.4 + flutter_clean_architecture: ^6.0.0 ``` From b154c959250f291e51ad03265ac4bbd12fa85938 Mon Sep 17 00:00:00 2001 From: Shady Boukhary Date: Tue, 2 Jul 2024 20:10:40 -0400 Subject: [PATCH 03/10] Update CI --- .github/workflows/ci.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6197572..432139c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,8 +3,9 @@ name: Continuous Integration on: [pull_request,workflow_dispatch] env: - flutter_version: "3.0.x" + flutter_version: "3.22.x" java_version: "12.x" + dart_sdk_version: ">=3.0.0 <4.0.0" jobs: analyze: From d81f5caf420ce0a79e77b9a4b408b99e73365ecf Mon Sep 17 00:00:00 2001 From: Shady Boukhary Date: Tue, 2 Jul 2024 20:14:20 -0400 Subject: [PATCH 04/10] Format files --- example/lib/src/app/pages/home/home_presenter.dart | 3 ++- example/lib/src/app/pages/home/home_view.dart | 3 ++- example/test/src/app/widgets/button_test.dart | 3 ++- lib/src/controller.dart | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/example/lib/src/app/pages/home/home_presenter.dart b/example/lib/src/app/pages/home/home_presenter.dart index 921d9e2..380c297 100644 --- a/example/lib/src/app/pages/home/home_presenter.dart +++ b/example/lib/src/app/pages/home/home_presenter.dart @@ -1,5 +1,6 @@ import '../../../domain/usecases/get_user_usecase.dart'; -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' as clean; +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' + as clean; class HomePresenter extends clean.Presenter { Function? getUserOnNext; diff --git a/example/lib/src/app/pages/home/home_view.dart b/example/lib/src/app/pages/home/home_view.dart index 0e2916a..601052d 100644 --- a/example/lib/src/app/pages/home/home_view.dart +++ b/example/lib/src/app/pages/home/home_view.dart @@ -1,6 +1,7 @@ import './home_controller.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' as clean; +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' + as clean; import '../../../data/repositories/data_users_repository.dart'; class HomePage extends clean.View { diff --git a/example/test/src/app/widgets/button_test.dart b/example/test/src/app/widgets/button_test.dart index 1515540..b62e328 100644 --- a/example/test/src/app/widgets/button_test.dart +++ b/example/test/src/app/widgets/button_test.dart @@ -2,7 +2,8 @@ import 'package:example/src/app/pages/home/home_controller.dart'; import 'package:example/src/app/widgets/button.dart'; import 'package:example/src/data/repositories/data_users_repository.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' as clean; +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' + as clean; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 8ba8492..afb8bf3 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' as clean; +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' + as clean; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; import 'package:provider/provider.dart'; From 4df06ece3d9ffaf03bacf3d7f29c5931a889b847 Mon Sep 17 00:00:00 2001 From: Shady Boukhary Date: Tue, 2 Jul 2024 20:24:04 -0400 Subject: [PATCH 05/10] Cleaning --- .github/workflows/ci.yaml | 2 +- analysis_options.yaml | 1 - example/lib/main.dart | 4 +++- .../src/app/pages/home/home_controller.dart | 14 ++++++++------ example/lib/src/app/widgets/button.dart | 2 +- lib/src/controller.dart | 18 ++++++++---------- lib/src/view.dart | 4 +++- 7 files changed, 24 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 432139c..359f514 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -48,7 +48,7 @@ jobs: - name: "Get dependencies" run: flutter packages get - name: "Formatter" - run: flutter format --set-exit-if-changed . + run: dart format --set-exit-if-changed . build_example: timeout-minutes: 15 diff --git a/analysis_options.yaml b/analysis_options.yaml index 8a17426..7e9e3aa 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,7 +4,6 @@ linter: rules: library_names: true avoid_empty_else: true - unawaited_futures: false avoid_print: false use_key_in_widget_constructors: false constant_identifier_names: false diff --git a/example/lib/main.dart b/example/lib/main.dart index c40509f..352d4d4 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,9 +3,11 @@ import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; import './src/app/pages/home/home_view.dart'; import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { + const MyApp({super.key}); + // This widget is the root of your application. @override Widget build(BuildContext context) { diff --git a/example/lib/src/app/pages/home/home_controller.dart b/example/lib/src/app/pages/home/home_controller.dart index 19d8533..1a38dfb 100644 --- a/example/lib/src/app/pages/home/home_controller.dart +++ b/example/lib/src/app/pages/home/home_controller.dart @@ -19,17 +19,17 @@ class HomeController extends Controller { // this is called automatically by the parent class void initListeners() { homePresenter.getUserOnNext = (User user) { - print(user.toString()); + logger.log(logger.level, user.toString()); _user = user; refreshUI(); // Refreshes the UI manually }; homePresenter.getUserOnComplete = () { - print('User retrieved'); + logger.log(logger.level, 'User retrieved'); }; // On error, show a snackbar, remove the user, and refresh the UI homePresenter.getUserOnError = (e) { - print('Could not retrieve user.'); + logger.log(logger.level, 'Could not retrieve user.'); ScaffoldMessenger.of(getContext()) .showSnackBar(SnackBar(content: Text(e.message))); _user = null; @@ -46,13 +46,15 @@ class HomeController extends Controller { } @override - void onResumed() => print('On resumed'); + void onResumed() => logger.log(logger.level, 'On resumed'); @override - void onReassembled() => print('View is about to be reassembled'); + void onReassembled() => + logger.log(logger.level, 'View is about to be reassembled'); @override - void onDeactivated() => print('View is about to be deactivated'); + void onDeactivated() => + logger.log(logger.level, 'View is about to be deactivated'); @override void onDisposed() { diff --git a/example/lib/src/app/widgets/button.dart b/example/lib/src/app/widgets/button.dart index e9d26b7..025b0c2 100644 --- a/example/lib/src/app/widgets/button.dart +++ b/example/lib/src/app/widgets/button.dart @@ -4,7 +4,7 @@ import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; class HomePageButton extends StatelessWidget { final String text; - const HomePageButton({required this.text}); + const HomePageButton({super.key, required this.text}); @override Widget build(BuildContext context) { diff --git a/lib/src/controller.dart b/lib/src/controller.dart index afb8bf3..230bfd2 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -1,15 +1,14 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' - as clean; +import 'package:flutter/material.dart' hide View; +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; import 'package:provider/provider.dart'; /// A Clean Architecture [Controller]. Should be aggregated within a `ViewState` or -/// a `clean.View`. However, it is preferable to be contained inside the `clean.View` for readability +/// a `View`. However, it is preferable to be contained inside the `View` for readability /// and maintainability. /// -/// The [Controller] handles the events triggered by the `clean.View`. For example, it handles +/// The [Controller] handles the events triggered by the `View`. For example, it handles /// the click events of buttons, lifecycle, data-sourcing, etc... /// /// The [Controller] is also route-aware. However, in order to use it, @@ -58,7 +57,7 @@ import 'package:provider/provider.dart'; /// return MaterialApp( /// title: 'Flutter Demo', /// home: Scaffold( -/// key: globalKey, // using the built-in global key of the `clean.View` for the scaffold or any other +/// key: globalKey, // using the built-in global key of the `View` for the scaffold or any other /// // widget provides the controller with a way to access them via getContext(), getState(), getStateKey() /// body: Column( /// children: [ @@ -81,7 +80,6 @@ abstract class Controller late Logger logger; late GlobalKey> _globalKey; - @mustCallSuper Controller() { logger = Logger('$runtimeType'); _isMounted = true; @@ -119,7 +117,7 @@ abstract class Controller } } - /// Unmounts the [Controller] from the `clean.View`. Called by the `clean.View` automatically. + /// Unmounts the [Controller] from the `View`. Called by the `View` automatically. /// Any cleaning, disposing should go in here. /// /// To perform correct actions that depends on latest [BuildContext] used on view before dispose, you must @@ -169,12 +167,12 @@ abstract class Controller } /// Initializes optional [Controller] variables that can be used for _refreshing and error displaying. - /// This method is called automatically by the mounted `clean.View`. Do not call. + /// This method is called automatically by the mounted `View`. Do not call. void initController(GlobalKey> key) { _globalKey = key; } - /// Retrieves the [BuildContext] associated with the `clean.View`. Will throw an error if initController() was not called prior. + /// Retrieves the [BuildContext] associated with the `View`. Will throw an error if initController() was not called prior. @protected BuildContext getContext() { assert(_globalKey.currentContext != null, diff --git a/lib/src/view.dart b/lib/src/view.dart index 766f0d2..90508f3 100644 --- a/lib/src/view.dart +++ b/lib/src/view.dart @@ -139,7 +139,9 @@ abstract class ViewState ViewState(this._controller) { _controller.initController(globalKey); - WidgetsBinding?.instance!.addObserver(_controller); + // ignore: invalid_null_aware_operator + WidgetsBinding?.instance! + .addObserver(_controller); // ignore:unnecessary_non_null_assertion _logger = Logger('$runtimeType'); } From 691c020a1c78719a3b57fe16ee882fc50d740494 Mon Sep 17 00:00:00 2001 From: Shady Boukhary Date: Tue, 2 Jul 2024 20:38:30 -0400 Subject: [PATCH 06/10] Update tests --- analysis_options.yaml | 2 +- test/background_usecase_test.dart | 16 ++++++++-------- test/controller_test.dart | 13 ++++++------- test/flutter_clean_architecture_test.dart | 10 +--------- test/responsive_view_test.dart | 2 +- test/usecase_test.dart | 8 ++++---- 6 files changed, 21 insertions(+), 30 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 7e9e3aa..30ca865 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -14,6 +14,6 @@ analyzer: invalid_use_of_visible_for_overriding_member: ignore exclude: - lib/generated_plugin_registrant.dart - - test/**_test.dart + # - test/**_test.dart enable-experiment: - extension-methods diff --git a/test/background_usecase_test.dart b/test/background_usecase_test.dart index ee5b95e..01625fd 100644 --- a/test/background_usecase_test.dart +++ b/test/background_usecase_test.dart @@ -6,7 +6,7 @@ void main() { test('BackgroundUseCase onNext and onDone.', () async { CounterUseCaseObserver observer = CounterUseCaseObserver(); CounterUseCase().execute(observer); - await Future.delayed(Duration(milliseconds: 500)); + await Future.delayed(const Duration(milliseconds: 500)); expect(observer.number, 2); expect(observer.done, true); expect(observer.error, false); @@ -15,7 +15,7 @@ void main() { test('BackgroundUseCase .OnError.', () async { CounterUseCaseObserver observer = CounterUseCaseObserver(); CounterUseCaseError().execute(observer); - await Future.delayed(Duration(milliseconds: 500)); + await Future.delayed(const Duration(milliseconds: 500)); expect(observer.number, -1); expect(observer.done, true); expect(observer.error, true); @@ -25,9 +25,9 @@ void main() { CounterUseCaseObserver observer = CounterUseCaseObserver(); CounterUseCaseCancelled usecase = CounterUseCaseCancelled() ..execute(observer); - await Future.delayed(Duration(milliseconds: 500)); + await Future.delayed(const Duration(milliseconds: 500)); usecase.dispose(); - await Future.delayed(Duration(milliseconds: 300)); + await Future.delayed(const Duration(milliseconds: 300)); expect(observer.number, 1); expect(observer.done, false); expect(observer.error, false); @@ -35,8 +35,8 @@ void main() { test('BackgroundUseCase matmul', () async { MatMulUseCaseObserver observer = MatMulUseCaseObserver(); - MatMulUseCase()..execute(observer, MatMulUseCaseParams.random()); - await Future.delayed(Duration(milliseconds: 400)); + MatMulUseCase().execute(observer, MatMulUseCaseParams.random()); + await Future.delayed(const Duration(milliseconds: 400)); }); }); } @@ -154,7 +154,7 @@ class MatMulUseCaseObserver extends Observer>> { @override void onNext(List>? mat) { - expect(mat?.first?.first, 2850.0); - expect(mat?.last?.last, 51855.0); + expect(mat?.first.first, 2850.0); + expect(mat?.last.last, 51855.0); } } diff --git a/test/controller_test.dart b/test/controller_test.dart index 5ba02b1..e695b6b 100644 --- a/test/controller_test.dart +++ b/test/controller_test.dart @@ -1,4 +1,4 @@ -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide View; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; @@ -16,8 +16,8 @@ void main() { testWidgets('Controller can change data and refresh View', (WidgetTester tester) async { - final binding = tester.binding; - binding.addTime(const Duration(seconds: 3)); + // await Future.delayed(const Duration(seconds: 3)); + await tester.pumpWidget(MaterialApp( home: CounterPage( onWidgetBuild: () { @@ -95,9 +95,8 @@ class CounterController extends Controller { refreshUI(); } - void showSnackBar() { - final scaffoldState = getState() as ScaffoldState; - scaffoldState.showSnackBar(SnackBar(content: Text('Hi'))); + void showSnackBar(context) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Hi'))); } @override @@ -176,7 +175,7 @@ class CounterState extends ViewState { ControlledWidgetBuilder( builder: (ctx, controller) { return MaterialButton( - key: snackBar, onPressed: () => controller.showSnackBar()); + key: snackBar, onPressed: () => controller.showSnackBar(context)); }, ), ], diff --git a/test/flutter_clean_architecture_test.dart b/test/flutter_clean_architecture_test.dart index 4fa7c3a..a5eb090 100644 --- a/test/flutter_clean_architecture_test.dart +++ b/test/flutter_clean_architecture_test.dart @@ -1,12 +1,4 @@ -import 'package:flutter_test/flutter_test.dart'; - -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; void main() { - test('adds one to input values', () { - final calculator = Calculator(); - expect(calculator.addOne(2), 3); - expect(calculator.addOne(-7), -6); - expect(calculator.addOne(0), 1); - }); + } diff --git a/test/responsive_view_test.dart b/test/responsive_view_test.dart index 821d7fc..ab72b71 100644 --- a/test/responsive_view_test.dart +++ b/test/responsive_view_test.dart @@ -1,4 +1,4 @@ -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide View; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; diff --git a/test/usecase_test.dart b/test/usecase_test.dart index 4f49fe0..185a206 100644 --- a/test/usecase_test.dart +++ b/test/usecase_test.dart @@ -6,7 +6,7 @@ void main() { test('UseCase onNext and onDone.', () async { var observer = CounterUseCaseObserver(); CounterUseCase().execute(observer); - await Future.delayed(Duration(milliseconds: 1000), () { + await Future.delayed(const Duration(milliseconds: 1000), () { expect(observer.number, 2); expect(observer.done, true); expect(observer.error, false); @@ -16,7 +16,7 @@ void main() { test('UseCase .OnError.', () async { var observer = CounterUseCaseObserver(); CounterUseCaseError().execute(observer); - await Future.delayed(Duration(milliseconds: 1000), () { + await Future.delayed(const Duration(milliseconds: 1000), () { expect(observer.number, -1); expect(observer.done, true); expect(observer.error, true); @@ -26,7 +26,7 @@ void main() { test('UseCase .dispose cancels the subscription', () async { var observer = CounterUseCaseObserver(); var usecase = CounterUseCase()..execute(observer); - await Future.delayed(Duration(milliseconds: 15), () { + await Future.delayed(const Duration(milliseconds: 15), () { usecase.dispose(); expect(observer.number, 0); expect(observer.done, false); @@ -39,7 +39,7 @@ void main() { class CounterUseCase extends UseCase { @override Future> buildUseCaseStream(void params) async { - return Stream.periodic(Duration(milliseconds: 10), (i) => i).take(3); + return Stream.periodic(const Duration(milliseconds: 10), (i) => i).take(3); } } From 0bdd380365974101b243916e261bf1e0bf7ff04f Mon Sep 17 00:00:00 2001 From: Shady Boukhary Date: Tue, 2 Jul 2024 20:40:38 -0400 Subject: [PATCH 07/10] Format files --- test/controller_test.dart | 6 ++++-- test/flutter_clean_architecture_test.dart | 5 +---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/test/controller_test.dart b/test/controller_test.dart index e695b6b..1cdbcdb 100644 --- a/test/controller_test.dart +++ b/test/controller_test.dart @@ -96,7 +96,8 @@ class CounterController extends Controller { } void showSnackBar(context) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Hi'))); + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar(content: Text('Hi'))); } @override @@ -175,7 +176,8 @@ class CounterState extends ViewState { ControlledWidgetBuilder( builder: (ctx, controller) { return MaterialButton( - key: snackBar, onPressed: () => controller.showSnackBar(context)); + key: snackBar, + onPressed: () => controller.showSnackBar(context)); }, ), ], diff --git a/test/flutter_clean_architecture_test.dart b/test/flutter_clean_architecture_test.dart index a5eb090..ab73b3a 100644 --- a/test/flutter_clean_architecture_test.dart +++ b/test/flutter_clean_architecture_test.dart @@ -1,4 +1 @@ - -void main() { - -} +void main() {} From 7588258faa2d46ba188c540e43686526f4a1556a Mon Sep 17 00:00:00 2001 From: Shady Boukhary Date: Tue, 2 Jul 2024 20:42:19 -0400 Subject: [PATCH 08/10] Update analyzer settings --- analysis_options.yaml | 2 +- lib/src/view.dart | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 30ca865..7e9e3aa 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -14,6 +14,6 @@ analyzer: invalid_use_of_visible_for_overriding_member: ignore exclude: - lib/generated_plugin_registrant.dart - # - test/**_test.dart + - test/**_test.dart enable-experiment: - extension-methods diff --git a/lib/src/view.dart b/lib/src/view.dart index 90508f3..a50216d 100644 --- a/lib/src/view.dart +++ b/lib/src/view.dart @@ -139,8 +139,7 @@ abstract class ViewState ViewState(this._controller) { _controller.initController(globalKey); - // ignore: invalid_null_aware_operator - WidgetsBinding?.instance! + WidgetsBinding?.instance // ignore: invalid_null_aware_operator .addObserver(_controller); // ignore:unnecessary_non_null_assertion _logger = Logger('$runtimeType'); } From 95c287f9fdfdc584a4fade10bfaf14b33875ec72 Mon Sep 17 00:00:00 2001 From: Shady Boukhary Date: Tue, 2 Jul 2024 20:46:23 -0400 Subject: [PATCH 09/10] Rename View to CleanView to avoid conflict with flutter --- example/lib/src/app/pages/home/home_view.dart | 17 +++++----- example/test/src/app/widgets/button_test.dart | 4 +-- lib/src/controller.dart | 32 +++++++++---------- lib/src/view.dart | 28 ++++++++-------- 4 files changed, 40 insertions(+), 41 deletions(-) diff --git a/example/lib/src/app/pages/home/home_view.dart b/example/lib/src/app/pages/home/home_view.dart index 601052d..ad44a80 100644 --- a/example/lib/src/app/pages/home/home_view.dart +++ b/example/lib/src/app/pages/home/home_view.dart @@ -1,10 +1,9 @@ import './home_controller.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_clean_architecture/flutter_clean_architecture.dart' - as clean; +import 'package:flutter_clean_architecture/flutter_clean_architecture.dart'; import '../../../data/repositories/data_users_repository.dart'; -class HomePage extends clean.View { +class HomePage extends CleanView { const HomePage({Key? key, this.title = ""}) : super(key: key); final String title; @@ -15,7 +14,7 @@ class HomePage extends clean.View { HomePageState(); } -class HomePageState extends clean.ViewState { +class HomePageState extends CleanViewState { HomePageState() : super(HomeController(DataUsersRepository())); @override @@ -31,7 +30,7 @@ class HomePageState extends clean.ViewState { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - clean.ControlledWidgetBuilder( + ControlledWidgetBuilder( builder: (context, controller) { return Text( 'Button pressed ${controller.counter} times.', @@ -41,7 +40,7 @@ class HomePageState extends clean.ViewState { const Text( 'The current user is', ), - clean.ControlledWidgetBuilder( + ControlledWidgetBuilder( builder: (context, controller) { return Text( controller.user == null ? '' : '${controller.user}', @@ -49,7 +48,7 @@ class HomePageState extends clean.ViewState { ); }, ), - clean.ControlledWidgetBuilder( + ControlledWidgetBuilder( builder: (context, controller) { return ElevatedButton( onPressed: controller.getUser, @@ -60,7 +59,7 @@ class HomePageState extends clean.ViewState { ); }, ), - clean.ControlledWidgetBuilder( + ControlledWidgetBuilder( builder: (context, controller) { return ElevatedButton( onPressed: controller.getUserwithError, @@ -75,7 +74,7 @@ class HomePageState extends clean.ViewState { ), ), ), - floatingActionButton: clean.ControlledWidgetBuilder( + floatingActionButton: ControlledWidgetBuilder( builder: (context, controller) { return FloatingActionButton( onPressed: () => controller.buttonPressed(), diff --git a/example/test/src/app/widgets/button_test.dart b/example/test/src/app/widgets/button_test.dart index b62e328..d50145d 100644 --- a/example/test/src/app/widgets/button_test.dart +++ b/example/test/src/app/widgets/button_test.dart @@ -26,7 +26,7 @@ void main() { }); } -class TestPage extends clean.View { +class TestPage extends clean.CleanView { const TestPage({ Key? key, }) : super(key: key); @@ -35,7 +35,7 @@ class TestPage extends clean.View { TestPageState createState() => TestPageState(); } -class TestPageState extends clean.ViewState { +class TestPageState extends clean.CleanViewState { TestPageState() : super(HomeController(DataUsersRepository())); @override diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 230bfd2..bcea5ac 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -5,10 +5,10 @@ import 'package:meta/meta.dart'; import 'package:provider/provider.dart'; /// A Clean Architecture [Controller]. Should be aggregated within a `ViewState` or -/// a `View`. However, it is preferable to be contained inside the `View` for readability +/// a `CleanView`. However, it is preferable to be contained inside the `CleanView` for readability /// and maintainability. /// -/// The [Controller] handles the events triggered by the `View`. For example, it handles +/// The [Controller] handles the events triggered by the `CleanView`. For example, it handles /// the click events of buttons, lifecycle, data-sourcing, etc... /// /// The [Controller] is also route-aware. However, in order to use it, @@ -57,7 +57,7 @@ import 'package:provider/provider.dart'; /// return MaterialApp( /// title: 'Flutter Demo', /// home: Scaffold( -/// key: globalKey, // using the built-in global key of the `View` for the scaffold or any other +/// key: globalKey, // using the built-in global key of the `CleanView` for the scaffold or any other /// // widget provides the controller with a way to access them via getContext(), getState(), getStateKey() /// body: Column( /// children: [ @@ -109,7 +109,7 @@ abstract class Controller } } - /// _refreshes the [ControlledWidgets] and the [StatefulWidgets] that depends on [FlutterCleanArchitecture.getController] of the [View] associated with the [Controller] if it is still mounted. + /// _refreshes the [ControlledWidgets] and the [StatefulWidgets] that depends on [FlutterCleanArchitecture.getController] of the [CleanView] associated with the [Controller] if it is still mounted. @protected void refreshUI() { if (_isMounted) { @@ -117,7 +117,7 @@ abstract class Controller } } - /// Unmounts the [Controller] from the `View`. Called by the `View` automatically. + /// Unmounts the [Controller] from the `CleanView`. Called by the `CleanView` automatically. /// Any cleaning, disposing should go in here. /// /// To perform correct actions that depends on latest [BuildContext] used on view before dispose, you must @@ -147,7 +147,7 @@ abstract class Controller super.dispose(); } - /// Retrieves the [State] associated with the [View] + /// Retrieves the [State] associated with the [CleanView] @protected State getState() { assert(_globalKey.currentState != null, @@ -160,19 +160,19 @@ abstract class Controller return _globalKey.currentState!; } - /// Retrieves the [GlobalKey>] associated with the [View] + /// Retrieves the [GlobalKey>] associated with the [CleanView] @protected GlobalKey> getStateKey() { return _globalKey; } /// Initializes optional [Controller] variables that can be used for _refreshing and error displaying. - /// This method is called automatically by the mounted `View`. Do not call. + /// This method is called automatically by the mounted `CleanView`. Do not call. void initController(GlobalKey> key) { _globalKey = key; } - /// Retrieves the [BuildContext] associated with the `View`. Will throw an error if initController() was not called prior. + /// Retrieves the [BuildContext] associated with the `CleanView`. Will throw an error if initController() was not called prior. @protected BuildContext getContext() { assert(_globalKey.currentContext != null, @@ -320,10 +320,10 @@ abstract class Controller @visibleForOverriding void onReassembled() {} - /// Called before [View.didChangeDependencies] is called + /// Called before [CleanView.didChangeDependencies] is called /// - /// Should be used when need to perform some action on [View.didChangeDependencies] life cycle. - /// [View.initViewState] should be called before the actions you need to perform. Like [didChangeDependencies], you can safely perform + /// Should be used when need to perform some action on [CleanView.didChangeDependencies] life cycle. + /// [CleanView.initViewState] should be called before the actions you need to perform. Like [didChangeDependencies], you can safely perform /// actions that depends on [BuildContext] here. /// /// ```dart @@ -335,9 +335,9 @@ abstract class Controller @visibleForOverriding void onDidChangeDependencies() {} - /// Called before [View.initState] is called + /// Called before [CleanView.initState] is called /// - /// Should be used when need to perform some action on [View.initState] life cycle. + /// Should be used when need to perform some action on [CleanView.initState] life cycle. /// /// ```dart /// class MyController extends Controller { @@ -355,9 +355,9 @@ typedef ControlledBuilder = Widget Function( /// This is a representation of a widget that is controlled by a [Controller] and needs to be re-rendered when /// [Controller.refreshUI] is triggered. /// -/// This was created to optimize the render cycle from a [ViewState]'s widget tree. +/// This was created to optimize the render cycle from a [CleanViewState]'s widget tree. /// -/// When [Controller.refreshUI] is called, only the ControlledWidgets inside [ViewState.view] will be re-rendered. +/// When [Controller.refreshUI] is called, only the ControlledWidgets inside [CleanViewState.view] will be re-rendered. /// /// Example: /// diff --git a/lib/src/view.dart b/lib/src/view.dart index a50216d..91e86ac 100644 --- a/lib/src/view.dart +++ b/lib/src/view.dart @@ -56,8 +56,8 @@ typedef ViewBuilder = Widget Function(BuildContext context); /// ``` /// /// You can optionally set globally new default values for breakpoints. To do so, just check on [FlutterCleanArchitecture.setDefaultViewBreakpoints] -abstract class ResponsiveViewState - extends ViewState { +abstract class ResponsiveViewState extends CleanViewState { ResponsiveViewState(Con controller) : super(controller); /// To be implemented by the developer which will build on [Watch ViewPort]. @@ -90,14 +90,14 @@ abstract class ResponsiveViewState } } -/// The [ViewState] represents the [State] of a [StatefulWidget], typically of a screen or a -/// page. The [ViewState] requires a [Controller] to handle its events and provide its data. +/// The [CleanViewState] represents the [State] of a [StatefulWidget], typically of a screen or a +/// page. The [CleanViewState] requires a [Controller] to handle its events and provide its data. /// -/// The [ViewState] also has a default [globalKey] that can be used inside its `build()` function +/// The [CleanViewState] also has a default [globalKey] that can be used inside its `build()` function /// in a widget to grant easy access to the [Controller], which could then use it to display /// snackbars, dialogs, and so on. /// -/// The [ViewState] lifecycle is also handled by the [Controller]. +/// The [CleanViewState] lifecycle is also handled by the [Controller]. /// ```dart /// class CounterState extends ViewState { /// CounterState(CounterController controller) : super(controller); @@ -126,7 +126,7 @@ abstract class ResponsiveViewState /// } /// /// ``` -abstract class ViewState +abstract class CleanViewState extends State { final GlobalKey> globalKey = GlobalKey>(); @@ -134,10 +134,10 @@ abstract class ViewState late Logger _logger; late ViewBuilder builder; - /// Implement the [Widget] you want to be displayed on [View] + /// Implement the [Widget] you want to be displayed on [CleanView] Widget get view; - ViewState(this._controller) { + CleanViewState(this._controller) { _controller.initController(globalKey); WidgetsBinding?.instance // ignore: invalid_null_aware_operator .addObserver(_controller); // ignore:unnecessary_non_null_assertion @@ -197,11 +197,11 @@ abstract class ViewState } } -/// The [View] represents a [StatefulWidget]. The [View] is typically a page or screen in -/// the application. However, a [View] can be any [StatefulWidget]. The [View] must have a +/// The [CleanView] represents a [StatefulWidget]. The [CleanView] is typically a page or screen in +/// the application. However, a [CleanView] can be any [StatefulWidget]. The [CleanView] must have a /// [State], and that [State] should be of type [ViewState]. /// -/// If a [RouteObserver] is given to the [View], it is used to register its [Controller] as +/// If a [RouteObserver] is given to the [CleanView], it is used to register its [Controller] as /// a subscriber, which provides the ability to listen to push and pop route events. /// ```dart /// class CounterPage extends View { @@ -213,10 +213,10 @@ abstract class ViewState /// /// ``` /// -abstract class View extends StatefulWidget { +abstract class CleanView extends StatefulWidget { @override final Key? key; final RouteObserver? routeObserver; - const View({this.routeObserver, this.key}) : super(key: key); + const CleanView({this.routeObserver, this.key}) : super(key: key); } From 343ffa0681fe42366902d43e4f7c5b2e27be97f7 Mon Sep 17 00:00:00 2001 From: Shady Boukhary Date: Tue, 2 Jul 2024 20:51:07 -0400 Subject: [PATCH 10/10] Update tests --- test/controller_test.dart | 4 ++-- test/responsive_view_test.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/controller_test.dart b/test/controller_test.dart index 1cdbcdb..be879f4 100644 --- a/test/controller_test.dart +++ b/test/controller_test.dart @@ -122,7 +122,7 @@ class CounterController extends Controller { } } -class CounterPage extends View { +class CounterPage extends CleanView { final CounterController controller; final Function onWidgetBuild; final Function onControlledWidgetBuild; @@ -138,7 +138,7 @@ class CounterPage extends View { State createState() => CounterState(controller: controller); } -class CounterState extends ViewState { +class CounterState extends CleanViewState { CounterState({required CounterController controller}) : super(controller); @override diff --git a/test/responsive_view_test.dart b/test/responsive_view_test.dart index ab72b71..0e3393a 100644 --- a/test/responsive_view_test.dart +++ b/test/responsive_view_test.dart @@ -137,7 +137,7 @@ class TestController extends Controller { } } -class TestPage extends View { +class TestPage extends CleanView { final TestController controller; TestPage({Key? key, required this.controller}) : super(key: key);