From 4e29120089d198fed111f7238682d322f0da2d74 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Tue, 7 Jan 2025 15:54:09 +0900 Subject: [PATCH 01/16] feat: Add utils_pagination package. --- .github/labeler.yml | 4 + .github/labels.yml | 4 + packages/utils/pagination/.gitignore | 27 ++++ packages/utils/pagination/.metadata | 10 ++ packages/utils/pagination/README.md | 1 + .../utils/pagination/analysis_options.yaml | 1 + packages/utils/pagination/lib/pagination.dart | 1 + .../pagination/lib/src/pagination_base.dart | 6 + packages/utils/pagination/pubspec.lock | 128 ++++++++++++++++++ packages/utils/pagination/pubspec.yaml | 15 ++ 10 files changed, 197 insertions(+) create mode 100644 packages/utils/pagination/.gitignore create mode 100644 packages/utils/pagination/.metadata create mode 100644 packages/utils/pagination/README.md create mode 100644 packages/utils/pagination/analysis_options.yaml create mode 100644 packages/utils/pagination/lib/pagination.dart create mode 100644 packages/utils/pagination/lib/src/pagination_base.dart create mode 100644 packages/utils/pagination/pubspec.lock create mode 100644 packages/utils/pagination/pubspec.yaml diff --git a/.github/labeler.yml b/.github/labeler.yml index 30f3f098..7b1383a8 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -59,6 +59,10 @@ labels: files: - packages/samples/github_repository/.+ + - label: "@packages/utils/pagination" + files: + - packages/utils/pagination/.+ + - label: "@apps/website" files: - apps/website/.+ diff --git a/.github/labels.yml b/.github/labels.yml index ba54d3b3..11b81181 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -99,6 +99,10 @@ description: packages samples github_repository package from_name: "@packages/features/github_repository" +- name: "@packages/utils/pagination" + color: 5d7dbc + description: packages utils pagination package + - name: "@apps/website" color: 3b4ff2 description: apps website package diff --git a/packages/utils/pagination/.gitignore b/packages/utils/pagination/.gitignore new file mode 100644 index 00000000..47c816ae --- /dev/null +++ b/packages/utils/pagination/.gitignore @@ -0,0 +1,27 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +build/ diff --git a/packages/utils/pagination/.metadata b/packages/utils/pagination/.metadata new file mode 100644 index 00000000..07d8623a --- /dev/null +++ b/packages/utils/pagination/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "2e9cb0aa71a386a91f73f7088d115c0d96654829" + channel: "stable" + +project_type: package diff --git a/packages/utils/pagination/README.md b/packages/utils/pagination/README.md new file mode 100644 index 00000000..f1e61303 --- /dev/null +++ b/packages/utils/pagination/README.md @@ -0,0 +1 @@ +# utils_pagination package diff --git a/packages/utils/pagination/analysis_options.yaml b/packages/utils/pagination/analysis_options.yaml new file mode 100644 index 00000000..d7db82f6 --- /dev/null +++ b/packages/utils/pagination/analysis_options.yaml @@ -0,0 +1 @@ +include: package:yumemi_lints/flutter/3.19/recommended.yaml diff --git a/packages/utils/pagination/lib/pagination.dart b/packages/utils/pagination/lib/pagination.dart new file mode 100644 index 00000000..528ee480 --- /dev/null +++ b/packages/utils/pagination/lib/pagination.dart @@ -0,0 +1 @@ +export 'src/pagination_base.dart'; diff --git a/packages/utils/pagination/lib/src/pagination_base.dart b/packages/utils/pagination/lib/src/pagination_base.dart new file mode 100644 index 00000000..e8a6f159 --- /dev/null +++ b/packages/utils/pagination/lib/src/pagination_base.dart @@ -0,0 +1,6 @@ +// TODO: Put public facing types in this file. + +/// Checks if you are awesome. Spoiler: you are. +class Awesome { + bool get isAwesome => true; +} diff --git a/packages/utils/pagination/pubspec.lock b/packages/utils/pagination/pubspec.lock new file mode 100644 index 00000000..37e82e70 --- /dev/null +++ b/packages/utils/pagination/pubspec.lock @@ -0,0 +1,128 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + args: + dependency: transitive + description: + name: args + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 + url: "https://pub.dev" + source: hosted + version: "2.6.0" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + collection: + dependency: transitive + description: + name: collection + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + url: "https://pub.dev" + source: hosted + version: "1.19.0" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" + yumemi_lints: + dependency: "direct dev" + description: + name: yumemi_lints + sha256: "5f88e2e5c052133631471b630bc8ef4cd77e7edef4d94b3a0d01d64470295c89" + url: "https://pub.dev" + source: hosted + version: "2.2.0" +sdks: + dart: ">=3.6.0 <4.0.0" + flutter: ">=3.27.1" diff --git a/packages/utils/pagination/pubspec.yaml b/packages/utils/pagination/pubspec.yaml new file mode 100644 index 00000000..4dd48e7e --- /dev/null +++ b/packages/utils/pagination/pubspec.yaml @@ -0,0 +1,15 @@ +name: utils_pagination +description: "This package provides pagination utilities." +publish_to: "none" +version: 0.0.1 + +environment: + sdk: ^3.6.0 + flutter: ^3.27.1 + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + yumemi_lints: ^2.2.0 From 90570ac2dccd662ca81ed46a1104a659de5f417a Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Wed, 8 Jan 2025 08:51:14 +0900 Subject: [PATCH 02/16] feat: Add dependencies to utils_pagination package. --- packages/utils/pagination/pubspec.lock | 556 ++++++++++++++++++ packages/utils/pagination/pubspec.yaml | 9 + .../utils/pagination/pubspec_overrides.yaml | 4 + 3 files changed, 569 insertions(+) create mode 100644 packages/utils/pagination/pubspec_overrides.yaml diff --git a/packages/utils/pagination/pubspec.lock b/packages/utils/pagination/pubspec.lock index 37e82e70..c56ab307 100644 --- a/packages/utils/pagination/pubspec.lock +++ b/packages/utils/pagination/pubspec.lock @@ -1,6 +1,35 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + url: "https://pub.dev" + source: hosted + version: "76.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.3" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + url: "https://pub.dev" + source: hosted + version: "6.11.0" + analyzer_plugin: + dependency: transitive + description: + name: analyzer_plugin + sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" + url: "https://pub.dev" + source: hosted + version: "0.11.3" args: dependency: transitive description: @@ -9,6 +38,86 @@ packages: url: "https://pub.dev" source: hosted version: "2.6.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "294a2edaf4814a378725bfe6358210196f5ea37af89ecd81bfa32960113d4948" + url: "https://pub.dev" + source: hosted + version: "4.0.3" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "99d3980049739a985cf9b21f30881f46db3ebc62c5b8d5e60e27440876b1ba1e" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "74691599a5bc750dc96a6b4bfd48f7d9d66453eab04c7f4063134800d6a5c573" + url: "https://pub.dev" + source: hosted + version: "2.4.14" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" + url: "https://pub.dev" + source: hosted + version: "8.0.0" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2" + url: "https://pub.dev" + source: hosted + version: "8.9.3" characters: dependency: transitive description: @@ -17,6 +126,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + url: "https://pub.dev" + source: hosted + version: "4.10.1" collection: dependency: transitive description: @@ -25,6 +166,69 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.0" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + cores_core: + dependency: "direct main" + description: + path: "../../cores/core" + relative: true + source: path + version: "0.0.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + custom_lint: + dependency: "direct dev" + description: + name: custom_lint + sha256: "3486c470bb93313a9417f926c7dd694a2e349220992d7b9d14534dc49c15bba9" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + custom_lint_builder: + dependency: transitive + description: + name: custom_lint_builder + sha256: "42cdc41994eeeddab0d7a722c7093ec52bd0761921eeb2cbdbf33d192a234759" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: "02450c3e45e2a6e8b26c4d16687596ab3c4644dd5792e3313aa9ceba5a49b7f5" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + custom_lint_visitor: + dependency: transitive + description: + name: custom_lint_visitor + sha256: bfe9b7a09c4775a587b58d10ebb871d4fe618237639b1e84d5ec62d7dfef25f9 + url: "https://pub.dev" + source: hosted + version: "1.0.0+6.11.0" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" + url: "https://pub.dev" + source: hosted + version: "2.3.7" file: dependency: transitive description: @@ -33,11 +237,139 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1" + url: "https://pub.dev" + source: hosted + version: "2.6.1" + freezed: + dependency: "direct dev" + description: + name: freezed + sha256: "44c19278dd9d89292cf46e97dc0c1e52ce03275f40a97c5a348e802a924bf40e" + url: "https://pub.dev" + source: hosted + version: "2.5.7" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e + url: "https://pub.dev" + source: hosted + version: "4.2.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + macros: + dependency: transitive + description: + name: macros + sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" + url: "https://pub.dev" + source: hosted + version: "0.1.3-main.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -54,6 +386,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67" + url: "https://pub.dev" + source: hosted + version: "2.1.1" path: dependency: transitive description: @@ -62,6 +410,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" pub_semver: dependency: transitive description: @@ -70,11 +426,91 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.5" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" + url: "https://pub.dev" + source: hosted + version: "2.6.1" + riverpod_analyzer_utils: + dependency: transitive + description: + name: riverpod_analyzer_utils + sha256: c6b8222b2b483cb87ae77ad147d6408f400c64f060df7a225b127f4afef4f8c8 + url: "https://pub.dev" + source: hosted + version: "0.5.8" + riverpod_annotation: + dependency: transitive + description: + name: riverpod_annotation + sha256: e14b0bf45b71326654e2705d462f21b958f987087be850afd60578fcd502d1b8 + url: "https://pub.dev" + source: hosted + version: "2.6.1" + riverpod_lint: + dependency: "direct dev" + description: + name: riverpod_lint + sha256: "83e4caa337a9840469b7b9bd8c2351ce85abad80f570d84146911b32086fbd99" + url: "https://pub.dev" + source: hosted + version: "2.6.3" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + simple_logger: + dependency: transitive + description: + name: simple_logger + sha256: "1de79f22bf31e5c33b91e9e302394dac02d8269d474848d33153c3a15c08e970" + url: "https://pub.dev" + source: hosted + version: "1.10.0" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.0" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" + source: hosted + version: "1.5.0" source_span: dependency: transitive description: @@ -83,6 +519,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + url: "https://pub.dev" + source: hosted + version: "1.12.0" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + url: "https://pub.dev" + source: hosted + version: "1.0.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" string_scanner: dependency: transitive description: @@ -99,6 +575,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + url: "https://pub.dev" + source: hosted + version: "0.7.3" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_math: dependency: transitive description: @@ -107,6 +615,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + visibility_detector: + dependency: "direct main" + description: + name: visibility_detector + sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 + url: "https://pub.dev" + source: hosted + version: "0.4.0+2" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + url: "https://pub.dev" + source: hosted + version: "14.3.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" + source: hosted + version: "3.0.1" yaml: dependency: transitive description: diff --git a/packages/utils/pagination/pubspec.yaml b/packages/utils/pagination/pubspec.yaml index 4dd48e7e..ecf1071d 100644 --- a/packages/utils/pagination/pubspec.yaml +++ b/packages/utils/pagination/pubspec.yaml @@ -8,8 +8,17 @@ environment: flutter: ^3.27.1 dependencies: + cores_core: + path: ../../cores/core flutter: sdk: flutter + flutter_riverpod: ^2.6.1 + freezed_annotation: ^2.4.4 + visibility_detector: ^0.4.0+2 dev_dependencies: + build_runner: ^2.4.11 + custom_lint: ^0.7.0 + freezed: ^2.5.2 + riverpod_lint: ^2.3.10 yumemi_lints: ^2.2.0 diff --git a/packages/utils/pagination/pubspec_overrides.yaml b/packages/utils/pagination/pubspec_overrides.yaml new file mode 100644 index 00000000..ae07ac2f --- /dev/null +++ b/packages/utils/pagination/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: cores_core +dependency_overrides: + cores_core: + path: ../../cores/core From 0a14d1c7df9b642ad744e4b46397ca3f54b8f3e0 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Wed, 8 Jan 2025 08:54:57 +0900 Subject: [PATCH 03/16] chore: Add setting files for utils_pagination package. --- packages/utils/pagination/analysis_options.yaml | 6 ++++++ packages/utils/pagination/build.yaml | 15 +++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 packages/utils/pagination/build.yaml diff --git a/packages/utils/pagination/analysis_options.yaml b/packages/utils/pagination/analysis_options.yaml index d7db82f6..a81b2133 100644 --- a/packages/utils/pagination/analysis_options.yaml +++ b/packages/utils/pagination/analysis_options.yaml @@ -1 +1,7 @@ include: package:yumemi_lints/flutter/3.19/recommended.yaml + +analyzer: + plugins: + # https://riverpod.dev/docs/introduction/getting_started#enabling-riverpod_lintcustom_lint + - custom_lint + \ No newline at end of file diff --git a/packages/utils/pagination/build.yaml b/packages/utils/pagination/build.yaml new file mode 100644 index 00000000..976a6868 --- /dev/null +++ b/packages/utils/pagination/build.yaml @@ -0,0 +1,15 @@ +targets: + $default: + builders: + freezed: + # /~https://github.com/dart-lang/build/blob/master/docs/faq.md#how-do-i-avoid-running-builders-on-unnecessary-inputs + generate_for: + include: + - lib/src/{model,**/model}/**.dart + # /~https://github.com/dart-lang/source_gen#ignore_for_file + source_gen:combining_builder: + options: + ignore_for_file: + - type=lint + - duplicate_ignore + - deprecated_member_use From ff6c0d8b5aeab7b9bb090865fa455007b08084d4 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Wed, 8 Jan 2025 08:59:28 +0900 Subject: [PATCH 04/16] feat: Reimplement paging feat to utils_pagination package. --- packages/utils/pagination/lib/pagination.dart | 4 +- .../lib/src/extension/async_value.dart | 50 ++++ .../pagination/lib/src/model/paging_data.dart | 28 ++ .../lib/src/model/paging_data.freezed.dart | 195 +++++++++++++ .../pagination/lib/src/pagination_base.dart | 6 - .../src/provider/paging_async_notifier.dart | 95 +++++++ .../lib/src/ui/common_paging_view.dart | 267 ++++++++++++++++++ 7 files changed, 638 insertions(+), 7 deletions(-) create mode 100644 packages/utils/pagination/lib/src/extension/async_value.dart create mode 100644 packages/utils/pagination/lib/src/model/paging_data.dart create mode 100644 packages/utils/pagination/lib/src/model/paging_data.freezed.dart delete mode 100644 packages/utils/pagination/lib/src/pagination_base.dart create mode 100644 packages/utils/pagination/lib/src/provider/paging_async_notifier.dart create mode 100644 packages/utils/pagination/lib/src/ui/common_paging_view.dart diff --git a/packages/utils/pagination/lib/pagination.dart b/packages/utils/pagination/lib/pagination.dart index 528ee480..636e706e 100644 --- a/packages/utils/pagination/lib/pagination.dart +++ b/packages/utils/pagination/lib/pagination.dart @@ -1 +1,3 @@ -export 'src/pagination_base.dart'; +export 'src/model/paging_data.dart'; +export 'src/provider/paging_async_notifier.dart'; +export 'src/ui/common_paging_view.dart'; diff --git a/packages/utils/pagination/lib/src/extension/async_value.dart b/packages/utils/pagination/lib/src/extension/async_value.dart new file mode 100644 index 00000000..5e50124d --- /dev/null +++ b/packages/utils/pagination/lib/src/extension/async_value.dart @@ -0,0 +1,50 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +extension AsyncValueExtension on AsyncValue { + /// Executes a future and returns its result as an [AsyncValue.data]. Captures + /// exceptions, preserving the previous state as [AsyncValue.error]. + Future> guardPreservingPreviousOnError( + Future Function() future, + ) async { + try { + return AsyncValue.data(await future()); + } on Exception catch (err, stack) { + return AsyncValue.error(err, stack).copyWithPrevious(this); + } + } + + /// Extends the [when] method to handle async data states more effectively, + /// especially when maintaining data integrity despite errors. + /// + /// Use `skipErrorOnHasValue` to retain and display existing data + /// even if subsequent fetch attempts result in errors, + /// ideal for maintaining a seamless user experience. + R whenIgnorableError({ + required R Function( + T data, { + required bool hasError, + required bool isLoading, + }) data, + required R Function(Object error, StackTrace stackTrace) error, + required R Function() loading, + bool skipLoadingOnReload = false, + bool skipLoadingOnRefresh = true, + bool skipError = false, + bool skipErrorOnHasValue = false, + }) { + if (skipErrorOnHasValue) { + if (hasValue && hasError) { + return data(requireValue, hasError: true, isLoading: isLoading); + } + } + + return when( + skipLoadingOnReload: skipLoadingOnReload, + skipLoadingOnRefresh: skipLoadingOnRefresh, + skipError: skipError, + data: (d) => data(d, hasError: hasError, isLoading: isLoading), + error: error, + loading: loading, + ); + } +} diff --git a/packages/utils/pagination/lib/src/model/paging_data.dart b/packages/utils/pagination/lib/src/model/paging_data.dart new file mode 100644 index 00000000..4410e6da --- /dev/null +++ b/packages/utils/pagination/lib/src/model/paging_data.dart @@ -0,0 +1,28 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'paging_data.freezed.dart'; + +/// Interface for a dataset capable of pagination. +/// Contains a list of items in [items] and a flag [hasMore] +/// indicating if more pages are available. +abstract interface class PagingData { + List get items; + bool get hasMore; +} + +/// Represents accumulated paginated data up to the current page. +/// +/// This class encapsulates a list of items from page one to the current page, +/// along with metadata about the paging status such as the current page number +/// and whether more pages are available. +@freezed +class PageBasedPagingData + with _$PageBasedPagingData + implements PagingData { + const factory PageBasedPagingData({ + required List items, + required int currentPage, + required bool hasMore, + }) = _PageBasedPagingData; + const PageBasedPagingData._(); +} diff --git a/packages/utils/pagination/lib/src/model/paging_data.freezed.dart b/packages/utils/pagination/lib/src/model/paging_data.freezed.dart new file mode 100644 index 00000000..286dcfd7 --- /dev/null +++ b/packages/utils/pagination/lib/src/model/paging_data.freezed.dart @@ -0,0 +1,195 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'paging_data.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: /~https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$PageBasedPagingData { + List get items => throw _privateConstructorUsedError; + int get currentPage => throw _privateConstructorUsedError; + bool get hasMore => throw _privateConstructorUsedError; + + /// Create a copy of PageBasedPagingData + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $PageBasedPagingDataCopyWith> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PageBasedPagingDataCopyWith { + factory $PageBasedPagingDataCopyWith(PageBasedPagingData value, + $Res Function(PageBasedPagingData) then) = + _$PageBasedPagingDataCopyWithImpl>; + @useResult + $Res call({List items, int currentPage, bool hasMore}); +} + +/// @nodoc +class _$PageBasedPagingDataCopyWithImpl> + implements $PageBasedPagingDataCopyWith { + _$PageBasedPagingDataCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of PageBasedPagingData + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? items = null, + Object? currentPage = null, + Object? hasMore = null, + }) { + return _then(_value.copyWith( + items: null == items + ? _value.items + : items // ignore: cast_nullable_to_non_nullable + as List, + currentPage: null == currentPage + ? _value.currentPage + : currentPage // ignore: cast_nullable_to_non_nullable + as int, + hasMore: null == hasMore + ? _value.hasMore + : hasMore // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$PageBasedPagingDataImplCopyWith + implements $PageBasedPagingDataCopyWith { + factory _$$PageBasedPagingDataImplCopyWith(_$PageBasedPagingDataImpl value, + $Res Function(_$PageBasedPagingDataImpl) then) = + __$$PageBasedPagingDataImplCopyWithImpl; + @override + @useResult + $Res call({List items, int currentPage, bool hasMore}); +} + +/// @nodoc +class __$$PageBasedPagingDataImplCopyWithImpl + extends _$PageBasedPagingDataCopyWithImpl> + implements _$$PageBasedPagingDataImplCopyWith { + __$$PageBasedPagingDataImplCopyWithImpl(_$PageBasedPagingDataImpl _value, + $Res Function(_$PageBasedPagingDataImpl) _then) + : super(_value, _then); + + /// Create a copy of PageBasedPagingData + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? items = null, + Object? currentPage = null, + Object? hasMore = null, + }) { + return _then(_$PageBasedPagingDataImpl( + items: null == items + ? _value._items + : items // ignore: cast_nullable_to_non_nullable + as List, + currentPage: null == currentPage + ? _value.currentPage + : currentPage // ignore: cast_nullable_to_non_nullable + as int, + hasMore: null == hasMore + ? _value.hasMore + : hasMore // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc + +class _$PageBasedPagingDataImpl extends _PageBasedPagingData { + const _$PageBasedPagingDataImpl( + {required final List items, + required this.currentPage, + required this.hasMore}) + : _items = items, + super._(); + + final List _items; + @override + List get items { + if (_items is EqualUnmodifiableListView) return _items; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_items); + } + + @override + final int currentPage; + @override + final bool hasMore; + + @override + String toString() { + return 'PageBasedPagingData<$T>(items: $items, currentPage: $currentPage, hasMore: $hasMore)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$PageBasedPagingDataImpl && + const DeepCollectionEquality().equals(other._items, _items) && + (identical(other.currentPage, currentPage) || + other.currentPage == currentPage) && + (identical(other.hasMore, hasMore) || other.hasMore == hasMore)); + } + + @override + int get hashCode => Object.hash(runtimeType, + const DeepCollectionEquality().hash(_items), currentPage, hasMore); + + /// Create a copy of PageBasedPagingData + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$PageBasedPagingDataImplCopyWith> + get copyWith => __$$PageBasedPagingDataImplCopyWithImpl>(this, _$identity); +} + +abstract class _PageBasedPagingData extends PageBasedPagingData { + const factory _PageBasedPagingData( + {required final List items, + required final int currentPage, + required final bool hasMore}) = _$PageBasedPagingDataImpl; + const _PageBasedPagingData._() : super._(); + + @override + List get items; + @override + int get currentPage; + @override + bool get hasMore; + + /// Create a copy of PageBasedPagingData + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$PageBasedPagingDataImplCopyWith> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/packages/utils/pagination/lib/src/pagination_base.dart b/packages/utils/pagination/lib/src/pagination_base.dart deleted file mode 100644 index e8a6f159..00000000 --- a/packages/utils/pagination/lib/src/pagination_base.dart +++ /dev/null @@ -1,6 +0,0 @@ -// TODO: Put public facing types in this file. - -/// Checks if you are awesome. Spoiler: you are. -class Awesome { - bool get isAwesome => true; -} diff --git a/packages/utils/pagination/lib/src/provider/paging_async_notifier.dart b/packages/utils/pagination/lib/src/provider/paging_async_notifier.dart new file mode 100644 index 00000000..cdaeb8eb --- /dev/null +++ b/packages/utils/pagination/lib/src/provider/paging_async_notifier.dart @@ -0,0 +1,95 @@ +import 'dart:async'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:utils_pagination/src/extension/async_value.dart'; +import 'package:utils_pagination/src/model/paging_data.dart'; + +/// An abstract notifier for paging logic, handling asynchronous data fetching +/// and state management. +/// +/// T: The type of the paginated data structure being managed. +/// U: The type of the items within the paginated data. +abstract class PagingAsyncNotifier, U> + extends AutoDisposeAsyncNotifier { + Future loadNext(); + + void forceRefresh() { + state = AsyncLoading(); + ref.invalidateSelf(); + } +} + +/// Extends [PagingAsyncNotifier] to implement page-based data fetching logic. +/// +/// Automatically handles fetching and appending data for subsequent pages. +/// Typically, only [fetch] needs to be overridden to define fetching logic +/// for the next page. +/// +/// T: The type of items in the paging data. +/// Usage Example: +/// ```dart +/// class MyPagingNotifier extends PageBasedPagingAsyncNotifier { +/// MyPagingNotifier() : super(initialPage: 0); // Start fetching from page 0 +/// +/// @override +/// Future> fetch({ +/// required int page, +/// }) async { +/// // Implement fetching logic here +/// return fetchMyDataType(page); +/// } +/// } +/// ``` +abstract class PageBasedPagingAsyncNotifier + extends PagingAsyncNotifier, T> { + PageBasedPagingAsyncNotifier({int initialPage = 1}) + : _initialPage = initialPage; + + final int _initialPage; + + Future> fetch({required int page}); + + @override + FutureOr> build() async { + final res = await fetch(page: _initialPage); + return PageBasedPagingData( + items: res.items, + currentPage: _initialPage, + hasMore: res.hasMore, + ); + } + + @override + Future loadNext() async { + final value = state.valueOrNull; + if (value == null) { + return; + } + + if (value.hasMore) { + state = AsyncLoading>().copyWithPrevious(state); + + state = await state.guardPreservingPreviousOnError( + () async { + final next = await fetch(page: value.currentPage + 1); + + return value.copyWith( + items: [...value.items, ...next.items], + currentPage: value.currentPage + 1, + hasMore: next.hasMore, + ); + }, + ); + } + } +} + +class PageBasedFetchResult { + const PageBasedFetchResult({ + required this.items, + required this.hasMore, + }); + + final List items; + final bool hasMore; +} diff --git a/packages/utils/pagination/lib/src/ui/common_paging_view.dart b/packages/utils/pagination/lib/src/ui/common_paging_view.dart new file mode 100644 index 00000000..11718153 --- /dev/null +++ b/packages/utils/pagination/lib/src/ui/common_paging_view.dart @@ -0,0 +1,267 @@ +import 'package:cores_core/exception.dart'; +import 'package:cores_core/util.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:utils_pagination/pagination.dart'; +import 'package:utils_pagination/src/extension/async_value.dart'; + +import 'package:visibility_detector/visibility_detector.dart'; + +class CommonPagingView, + D extends PagingData, T> extends ConsumerWidget { + /// A widget for displaying paginated data with asynchronous fetching + /// capabilities, including pull-to-refresh support. + /// + /// Features: + /// 1. Displays widgets created by [_contentBuilder] with available data. + /// 2. Shows a CircularProgressIndicator while loading the first page. + /// 3. Presents an error widget for first page load failures. + /// 4. Loads the next page when the last item is displayed. + /// 5. Handles errors with an optional callback [_onError] for non-initial + /// loads. + /// 6. Enables pull-to-refresh for manual data refresh. + /// + /// Generics: + /// N: Notifier type extending [PagingAsyncNotifier] for data fetching. + /// D: Type of paginated data which the notifier handles. + /// T: Type of individual items within the paginated data. + /// + /// Example: + /// ```dart + /// CommonPagingView( + /// provider: myNotifierProvider, + /// contentBuilder: (data, endItem) => ListView.builder( + /// itemCount: data.items.length + (endItem != null ? 1 : 0), + /// itemBuilder: (context, index) { + /// if (index == data.items.length && endItem != null) { + /// return endItem; + /// } + /// return ListTile(title: Text(data.items[index].name)); + /// }, + /// ), + /// onError: (e) => showErrorDialog(context, e), + /// ) + /// ``` + const CommonPagingView({ + required AutoDisposeAsyncNotifierProvider provider, + required Widget Function(D data, Widget? endItem) contentBuilder, + required void Function(AppException e) onError, + super.key, + }) : _contentBuilder = contentBuilder, + _provider = provider, + _onError = onError; + + /// Specifies the provider for a [PagingAsyncNotifier] implemented class. + final AutoDisposeAsyncNotifierProvider _provider; + + /// Specifies a function to build widgets for displaying paginated data. + /// The function may receive an optional `endItem` widget, which serves as a + /// marker for the end of the list. If provided and non-null, `endItem` should + /// be included as the final item. This helps in detecting the list's end or + /// adding a special widget at that position. + final Widget Function(D data, Widget? endItem) _contentBuilder; + + final void Function(AppException e) _onError; + + Widget? _endItem( + D data, + WidgetRef ref, { + required bool hasError, + required bool isLoading, + }) { + if (!data.hasMore) { + return null; + } + + if (hasError && isLoading) { + return const _ReloadingItem(); + } + + if (hasError && !isLoading) { + return _EndItemWhenError( + onPressed: () async => ref.read(_provider.notifier).loadNext(), + ); + } + + return _EndItem( + onScrollEnd: () async => ref.read(_provider.notifier).loadNext(), + ); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + ref.listen( + _provider, + (_, next) { + if (!next.isLoading && next.hasError) { + final error = next.error; + if (error is AppException) { + _onError(error); + return; + } + // AppException is expected, so this should never be reached. + logger.shout( + 'Unexpected error type encountered: $error - ' + 'This indicates a need to revise exception handling to ensure only ' + 'AppException is thrown. Please review exception handling ' + 'practices and modify as necessary.', + ); + } + }, + ); + + return ref.watch(_provider).whenIgnorableError( + data: ( + data, { + required hasError, + required isLoading, + }) { + return RefreshIndicator( + onRefresh: () async => ref.refresh(_provider.future), + child: _contentBuilder( + // Displays EndItem to detect scroll end + // if more data is available and no errors. + data, + _endItem( + data, + ref, + hasError: hasError, + isLoading: isLoading, + ), + ), + ); + }, + loading: () { + // Shows loading indicator during initial page load. + return const _LoadingItem(); + }, + error: (e, _) { + // Displays error message for initial load failures. + return _ErrorItem( + errorMessage: e.toString(), + onError: () => ref.read(_provider.notifier).forceRefresh(), + ); + }, + // Continues showing data on subsequent load errors, + // without hiding existing content. + skipErrorOnHasValue: true, + ); + } +} + +class _ReloadingItem extends StatelessWidget { + const _ReloadingItem(); + @override + Widget build(BuildContext context) { + return const Center( + child: Padding( + padding: EdgeInsets.all(16), + child: CircularProgressIndicator(), + ), + ); + } +} + +class _EndItem extends StatelessWidget { + const _EndItem({ + required void Function() onScrollEnd, + }) : _onScrollEnd = onScrollEnd; + final VoidCallback _onScrollEnd; + + @override + Widget build(BuildContext context) { + return VisibilityDetector( + key: key ?? const Key('EndItem'), + onVisibilityChanged: (info) { + if (info.visibleFraction > 0.1) { + _onScrollEnd(); + } + }, + child: const Center( + child: Padding( + padding: EdgeInsets.all(16), + child: CircularProgressIndicator(), + ), + ), + ); + } +} + +class _ErrorItem extends StatelessWidget { + const _ErrorItem({ + required String errorMessage, + required VoidCallback onError, + }) : _errorMessage = errorMessage, + _onError = onError; + + final String _errorMessage; + final VoidCallback _onError; + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + onPressed: _onError, + icon: const Icon(Icons.refresh), + ), + Text(_errorMessage), + ], + ), + ); + } +} + +class _LoadingItem extends StatelessWidget { + const _LoadingItem(); + + @override + Widget build(BuildContext context) => + const Center(child: CircularProgressIndicator()); +} + +class _EndItemWhenError extends StatelessWidget { + const _EndItemWhenError({required void Function() onPressed}) + : _onPressed = onPressed; + final void Function() _onPressed; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 130, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const Flexible( + flex: 3, + child: Center( + child: Icon(Icons.cloud_off), + ), + ), + const Flexible( + flex: 2, + child: Center( + child: Text( + 'Please check your connection and try again.', + ), + ), + ), + Flexible( + flex: 3, + child: Center( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: const StadiumBorder(), + ), + onPressed: _onPressed, + child: const Text('refresh'), + ), + ), + ), + ], + ), + ); + } +} From 3e91ea387e6b23d308c8c7d461bf167ba80e23bf Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Wed, 8 Jan 2025 09:03:13 +0900 Subject: [PATCH 05/16] refactor: Change to use pagination feat in utils_pagination instead of cores_core. --- .../lib/src/ui/github_repository_list_page.dart | 2 +- .../src/ui/provider/github_repository_list_notifier.dart | 2 +- packages/samples/github_repository/pubspec.lock | 7 +++++++ packages/samples/github_repository/pubspec.yaml | 2 ++ samples/github_app/pubspec.lock | 7 +++++++ 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart b/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart index 4cfbeadc..1b1eec2a 100644 --- a/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart +++ b/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart @@ -1,5 +1,4 @@ import 'package:cores_core/exception.dart'; -import 'package:cores_core/ui.dart'; import 'package:cores_designsystem/components.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -8,6 +7,7 @@ import 'package:samples_github_repository/src/data/provider/scroll_notifier.dart import 'package:samples_github_repository/src/domain/model/github_repository.dart'; import 'package:samples_github_repository/src/ui/provider/github_repository_list_notifier.dart'; import 'package:samples_github_repository/ui.dart'; +import 'package:utils_pagination/pagination.dart'; abstract interface class GitHubRepositoryListPageNavigator { void goGitHubRepositoryDetailPage( diff --git a/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart b/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart index fa5f07d4..ac7c1881 100644 --- a/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart +++ b/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart @@ -1,7 +1,7 @@ -import 'package:cores_core/pagination.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:samples_github_repository/src/data/provider/repository.dart'; import 'package:samples_github_repository/src/domain/model/github_repository.dart'; +import 'package:utils_pagination/pagination.dart'; final pageBasedGitHubRepositoryNotifierProvider = AsyncNotifierProvider .autoDispose( diff --git a/packages/samples/github_repository/pubspec.lock b/packages/samples/github_repository/pubspec.lock index bce6adc0..91639eb5 100644 --- a/packages/samples/github_repository/pubspec.lock +++ b/packages/samples/github_repository/pubspec.lock @@ -793,6 +793,13 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + utils_pagination: + dependency: "direct main" + description: + path: "../../utils/pagination" + relative: true + source: path + version: "0.0.1" uuid: dependency: transitive description: diff --git a/packages/samples/github_repository/pubspec.yaml b/packages/samples/github_repository/pubspec.yaml index 13cc3db3..ea0cddfc 100644 --- a/packages/samples/github_repository/pubspec.yaml +++ b/packages/samples/github_repository/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: hooks_riverpod: ^2.5.1 json_annotation: ^4.9.0 riverpod_annotation: ^2.6.1 + utils_pagination: + path: ../../utils/pagination dev_dependencies: build_runner: ^2.4.11 diff --git a/samples/github_app/pubspec.lock b/samples/github_app/pubspec.lock index 2cf9a6c4..96630d77 100644 --- a/samples/github_app/pubspec.lock +++ b/samples/github_app/pubspec.lock @@ -456,6 +456,13 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + utils_pagination: + dependency: "direct overridden" + description: + path: "../../packages/utils/pagination" + relative: true + source: path + version: "0.0.1" vector_math: dependency: transitive description: From 2cbaca883f33d6727580a179ca68d4d061db0d03 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Wed, 8 Jan 2025 09:21:09 +0900 Subject: [PATCH 06/16] chore: Remove pagination feat from cores_core. --- .../cores/core/lib/extension/async_value.dart | 1 - packages/cores/core/lib/pagination.dart | 2 - .../core/lib/src/extension/async_value.dart | 50 ---- .../lib/src/pagination/model/paging_data.dart | 28 -- .../pagination/model/paging_data.freezed.dart | 195 ------------- .../provider/paging_async_notifier.dart | 95 ------- .../lib/src/ui/util/common_paging_view.dart | 268 ------------------ packages/cores/core/lib/ui.dart | 1 - 8 files changed, 640 deletions(-) delete mode 100644 packages/cores/core/lib/extension/async_value.dart delete mode 100644 packages/cores/core/lib/pagination.dart delete mode 100644 packages/cores/core/lib/src/extension/async_value.dart delete mode 100644 packages/cores/core/lib/src/pagination/model/paging_data.dart delete mode 100644 packages/cores/core/lib/src/pagination/model/paging_data.freezed.dart delete mode 100644 packages/cores/core/lib/src/pagination/provider/paging_async_notifier.dart delete mode 100644 packages/cores/core/lib/src/ui/util/common_paging_view.dart diff --git a/packages/cores/core/lib/extension/async_value.dart b/packages/cores/core/lib/extension/async_value.dart deleted file mode 100644 index 05b48a53..00000000 --- a/packages/cores/core/lib/extension/async_value.dart +++ /dev/null @@ -1 +0,0 @@ -export '../src/extension/async_value.dart'; diff --git a/packages/cores/core/lib/pagination.dart b/packages/cores/core/lib/pagination.dart deleted file mode 100644 index b6a95875..00000000 --- a/packages/cores/core/lib/pagination.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'src/pagination/model/paging_data.dart'; -export 'src/pagination/provider/paging_async_notifier.dart'; diff --git a/packages/cores/core/lib/src/extension/async_value.dart b/packages/cores/core/lib/src/extension/async_value.dart deleted file mode 100644 index 5e50124d..00000000 --- a/packages/cores/core/lib/src/extension/async_value.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -extension AsyncValueExtension on AsyncValue { - /// Executes a future and returns its result as an [AsyncValue.data]. Captures - /// exceptions, preserving the previous state as [AsyncValue.error]. - Future> guardPreservingPreviousOnError( - Future Function() future, - ) async { - try { - return AsyncValue.data(await future()); - } on Exception catch (err, stack) { - return AsyncValue.error(err, stack).copyWithPrevious(this); - } - } - - /// Extends the [when] method to handle async data states more effectively, - /// especially when maintaining data integrity despite errors. - /// - /// Use `skipErrorOnHasValue` to retain and display existing data - /// even if subsequent fetch attempts result in errors, - /// ideal for maintaining a seamless user experience. - R whenIgnorableError({ - required R Function( - T data, { - required bool hasError, - required bool isLoading, - }) data, - required R Function(Object error, StackTrace stackTrace) error, - required R Function() loading, - bool skipLoadingOnReload = false, - bool skipLoadingOnRefresh = true, - bool skipError = false, - bool skipErrorOnHasValue = false, - }) { - if (skipErrorOnHasValue) { - if (hasValue && hasError) { - return data(requireValue, hasError: true, isLoading: isLoading); - } - } - - return when( - skipLoadingOnReload: skipLoadingOnReload, - skipLoadingOnRefresh: skipLoadingOnRefresh, - skipError: skipError, - data: (d) => data(d, hasError: hasError, isLoading: isLoading), - error: error, - loading: loading, - ); - } -} diff --git a/packages/cores/core/lib/src/pagination/model/paging_data.dart b/packages/cores/core/lib/src/pagination/model/paging_data.dart deleted file mode 100644 index 4410e6da..00000000 --- a/packages/cores/core/lib/src/pagination/model/paging_data.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'paging_data.freezed.dart'; - -/// Interface for a dataset capable of pagination. -/// Contains a list of items in [items] and a flag [hasMore] -/// indicating if more pages are available. -abstract interface class PagingData { - List get items; - bool get hasMore; -} - -/// Represents accumulated paginated data up to the current page. -/// -/// This class encapsulates a list of items from page one to the current page, -/// along with metadata about the paging status such as the current page number -/// and whether more pages are available. -@freezed -class PageBasedPagingData - with _$PageBasedPagingData - implements PagingData { - const factory PageBasedPagingData({ - required List items, - required int currentPage, - required bool hasMore, - }) = _PageBasedPagingData; - const PageBasedPagingData._(); -} diff --git a/packages/cores/core/lib/src/pagination/model/paging_data.freezed.dart b/packages/cores/core/lib/src/pagination/model/paging_data.freezed.dart deleted file mode 100644 index 286dcfd7..00000000 --- a/packages/cores/core/lib/src/pagination/model/paging_data.freezed.dart +++ /dev/null @@ -1,195 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'paging_data.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: /~https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -/// @nodoc -mixin _$PageBasedPagingData { - List get items => throw _privateConstructorUsedError; - int get currentPage => throw _privateConstructorUsedError; - bool get hasMore => throw _privateConstructorUsedError; - - /// Create a copy of PageBasedPagingData - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $PageBasedPagingDataCopyWith> get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $PageBasedPagingDataCopyWith { - factory $PageBasedPagingDataCopyWith(PageBasedPagingData value, - $Res Function(PageBasedPagingData) then) = - _$PageBasedPagingDataCopyWithImpl>; - @useResult - $Res call({List items, int currentPage, bool hasMore}); -} - -/// @nodoc -class _$PageBasedPagingDataCopyWithImpl> - implements $PageBasedPagingDataCopyWith { - _$PageBasedPagingDataCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of PageBasedPagingData - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? items = null, - Object? currentPage = null, - Object? hasMore = null, - }) { - return _then(_value.copyWith( - items: null == items - ? _value.items - : items // ignore: cast_nullable_to_non_nullable - as List, - currentPage: null == currentPage - ? _value.currentPage - : currentPage // ignore: cast_nullable_to_non_nullable - as int, - hasMore: null == hasMore - ? _value.hasMore - : hasMore // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$PageBasedPagingDataImplCopyWith - implements $PageBasedPagingDataCopyWith { - factory _$$PageBasedPagingDataImplCopyWith(_$PageBasedPagingDataImpl value, - $Res Function(_$PageBasedPagingDataImpl) then) = - __$$PageBasedPagingDataImplCopyWithImpl; - @override - @useResult - $Res call({List items, int currentPage, bool hasMore}); -} - -/// @nodoc -class __$$PageBasedPagingDataImplCopyWithImpl - extends _$PageBasedPagingDataCopyWithImpl> - implements _$$PageBasedPagingDataImplCopyWith { - __$$PageBasedPagingDataImplCopyWithImpl(_$PageBasedPagingDataImpl _value, - $Res Function(_$PageBasedPagingDataImpl) _then) - : super(_value, _then); - - /// Create a copy of PageBasedPagingData - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? items = null, - Object? currentPage = null, - Object? hasMore = null, - }) { - return _then(_$PageBasedPagingDataImpl( - items: null == items - ? _value._items - : items // ignore: cast_nullable_to_non_nullable - as List, - currentPage: null == currentPage - ? _value.currentPage - : currentPage // ignore: cast_nullable_to_non_nullable - as int, - hasMore: null == hasMore - ? _value.hasMore - : hasMore // ignore: cast_nullable_to_non_nullable - as bool, - )); - } -} - -/// @nodoc - -class _$PageBasedPagingDataImpl extends _PageBasedPagingData { - const _$PageBasedPagingDataImpl( - {required final List items, - required this.currentPage, - required this.hasMore}) - : _items = items, - super._(); - - final List _items; - @override - List get items { - if (_items is EqualUnmodifiableListView) return _items; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_items); - } - - @override - final int currentPage; - @override - final bool hasMore; - - @override - String toString() { - return 'PageBasedPagingData<$T>(items: $items, currentPage: $currentPage, hasMore: $hasMore)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$PageBasedPagingDataImpl && - const DeepCollectionEquality().equals(other._items, _items) && - (identical(other.currentPage, currentPage) || - other.currentPage == currentPage) && - (identical(other.hasMore, hasMore) || other.hasMore == hasMore)); - } - - @override - int get hashCode => Object.hash(runtimeType, - const DeepCollectionEquality().hash(_items), currentPage, hasMore); - - /// Create a copy of PageBasedPagingData - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$PageBasedPagingDataImplCopyWith> - get copyWith => __$$PageBasedPagingDataImplCopyWithImpl>(this, _$identity); -} - -abstract class _PageBasedPagingData extends PageBasedPagingData { - const factory _PageBasedPagingData( - {required final List items, - required final int currentPage, - required final bool hasMore}) = _$PageBasedPagingDataImpl; - const _PageBasedPagingData._() : super._(); - - @override - List get items; - @override - int get currentPage; - @override - bool get hasMore; - - /// Create a copy of PageBasedPagingData - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$PageBasedPagingDataImplCopyWith> - get copyWith => throw _privateConstructorUsedError; -} diff --git a/packages/cores/core/lib/src/pagination/provider/paging_async_notifier.dart b/packages/cores/core/lib/src/pagination/provider/paging_async_notifier.dart deleted file mode 100644 index 88d93792..00000000 --- a/packages/cores/core/lib/src/pagination/provider/paging_async_notifier.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'dart:async'; - -import 'package:cores_core/extension/async_value.dart'; -import 'package:cores_core/src/pagination/model/paging_data.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -/// An abstract notifier for paging logic, handling asynchronous data fetching -/// and state management. -/// -/// T: The type of the paginated data structure being managed. -/// U: The type of the items within the paginated data. -abstract class PagingAsyncNotifier, U> - extends AutoDisposeAsyncNotifier { - Future loadNext(); - - void forceRefresh() { - state = AsyncLoading(); - ref.invalidateSelf(); - } -} - -/// Extends [PagingAsyncNotifier] to implement page-based data fetching logic. -/// -/// Automatically handles fetching and appending data for subsequent pages. -/// Typically, only [fetch] needs to be overridden to define fetching logic -/// for the next page. -/// -/// T: The type of items in the paging data. -/// Usage Example: -/// ```dart -/// class MyPagingNotifier extends PageBasedPagingAsyncNotifier { -/// MyPagingNotifier() : super(initialPage: 0); // Start fetching from page 0 -/// -/// @override -/// Future> fetch({ -/// required int page, -/// }) async { -/// // Implement fetching logic here -/// return fetchMyDataType(page); -/// } -/// } -/// ``` -abstract class PageBasedPagingAsyncNotifier - extends PagingAsyncNotifier, T> { - PageBasedPagingAsyncNotifier({int initialPage = 1}) - : _initialPage = initialPage; - - final int _initialPage; - - Future> fetch({required int page}); - - @override - FutureOr> build() async { - final res = await fetch(page: _initialPage); - return PageBasedPagingData( - items: res.items, - currentPage: _initialPage, - hasMore: res.hasMore, - ); - } - - @override - Future loadNext() async { - final value = state.valueOrNull; - if (value == null) { - return; - } - - if (value.hasMore) { - state = AsyncLoading>().copyWithPrevious(state); - - state = await state.guardPreservingPreviousOnError( - () async { - final next = await fetch(page: value.currentPage + 1); - - return value.copyWith( - items: [...value.items, ...next.items], - currentPage: value.currentPage + 1, - hasMore: next.hasMore, - ); - }, - ); - } - } -} - -class PageBasedFetchResult { - const PageBasedFetchResult({ - required this.items, - required this.hasMore, - }); - - final List items; - final bool hasMore; -} diff --git a/packages/cores/core/lib/src/ui/util/common_paging_view.dart b/packages/cores/core/lib/src/ui/util/common_paging_view.dart deleted file mode 100644 index 2283f384..00000000 --- a/packages/cores/core/lib/src/ui/util/common_paging_view.dart +++ /dev/null @@ -1,268 +0,0 @@ -import 'package:cores_core/exception.dart'; -import 'package:cores_core/extension/async_value.dart'; -import 'package:cores_core/src/pagination/model/paging_data.dart'; -import 'package:cores_core/src/pagination/provider/paging_async_notifier.dart'; -import 'package:cores_core/src/util/logger.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'package:visibility_detector/visibility_detector.dart'; - -class CommonPagingView, - D extends PagingData, T> extends ConsumerWidget { - /// A widget for displaying paginated data with asynchronous fetching - /// capabilities, including pull-to-refresh support. - /// - /// Features: - /// 1. Displays widgets created by [_contentBuilder] with available data. - /// 2. Shows a CircularProgressIndicator while loading the first page. - /// 3. Presents an error widget for first page load failures. - /// 4. Loads the next page when the last item is displayed. - /// 5. Handles errors with an optional callback [_onError] for non-initial - /// loads. - /// 6. Enables pull-to-refresh for manual data refresh. - /// - /// Generics: - /// N: Notifier type extending [PagingAsyncNotifier] for data fetching. - /// D: Type of paginated data which the notifier handles. - /// T: Type of individual items within the paginated data. - /// - /// Example: - /// ```dart - /// CommonPagingView( - /// provider: myNotifierProvider, - /// contentBuilder: (data, endItem) => ListView.builder( - /// itemCount: data.items.length + (endItem != null ? 1 : 0), - /// itemBuilder: (context, index) { - /// if (index == data.items.length && endItem != null) { - /// return endItem; - /// } - /// return ListTile(title: Text(data.items[index].name)); - /// }, - /// ), - /// onError: (e) => showErrorDialog(context, e), - /// ) - /// ``` - const CommonPagingView({ - required AutoDisposeAsyncNotifierProvider provider, - required Widget Function(D data, Widget? endItem) contentBuilder, - required void Function(AppException e) onError, - super.key, - }) : _contentBuilder = contentBuilder, - _provider = provider, - _onError = onError; - - /// Specifies the provider for a [PagingAsyncNotifier] implemented class. - final AutoDisposeAsyncNotifierProvider _provider; - - /// Specifies a function to build widgets for displaying paginated data. - /// The function may receive an optional `endItem` widget, which serves as a - /// marker for the end of the list. If provided and non-null, `endItem` should - /// be included as the final item. This helps in detecting the list's end or - /// adding a special widget at that position. - final Widget Function(D data, Widget? endItem) _contentBuilder; - - final void Function(AppException e) _onError; - - Widget? _endItem( - D data, - WidgetRef ref, { - required bool hasError, - required bool isLoading, - }) { - if (!data.hasMore) { - return null; - } - - if (hasError && isLoading) { - return const _ReloadingItem(); - } - - if (hasError && !isLoading) { - return _EndItemWhenError( - onPressed: () async => ref.read(_provider.notifier).loadNext(), - ); - } - - return _EndItem( - onScrollEnd: () async => ref.read(_provider.notifier).loadNext(), - ); - } - - @override - Widget build(BuildContext context, WidgetRef ref) { - ref.listen( - _provider, - (_, next) { - if (!next.isLoading && next.hasError) { - final error = next.error; - if (error is AppException) { - _onError(error); - return; - } - // AppException is expected, so this should never be reached. - logger.shout( - 'Unexpected error type encountered: $error - ' - 'This indicates a need to revise exception handling to ensure only ' - 'AppException is thrown. Please review exception handling ' - 'practices and modify as necessary.', - ); - } - }, - ); - - return ref.watch(_provider).whenIgnorableError( - data: ( - data, { - required hasError, - required isLoading, - }) { - return RefreshIndicator( - onRefresh: () async => ref.refresh(_provider.future), - child: _contentBuilder( - // Displays EndItem to detect scroll end - // if more data is available and no errors. - data, - _endItem( - data, - ref, - hasError: hasError, - isLoading: isLoading, - ), - ), - ); - }, - loading: () { - // Shows loading indicator during initial page load. - return const _LoadingItem(); - }, - error: (e, _) { - // Displays error message for initial load failures. - return _ErrorItem( - errorMessage: e.toString(), - onError: () => ref.read(_provider.notifier).forceRefresh(), - ); - }, - // Continues showing data on subsequent load errors, - // without hiding existing content. - skipErrorOnHasValue: true, - ); - } -} - -class _ReloadingItem extends StatelessWidget { - const _ReloadingItem(); - @override - Widget build(BuildContext context) { - return const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: CircularProgressIndicator(), - ), - ); - } -} - -class _EndItem extends StatelessWidget { - const _EndItem({ - required void Function() onScrollEnd, - }) : _onScrollEnd = onScrollEnd; - final VoidCallback _onScrollEnd; - - @override - Widget build(BuildContext context) { - return VisibilityDetector( - key: key ?? const Key('EndItem'), - onVisibilityChanged: (info) { - if (info.visibleFraction > 0.1) { - _onScrollEnd(); - } - }, - child: const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: CircularProgressIndicator(), - ), - ), - ); - } -} - -class _ErrorItem extends StatelessWidget { - const _ErrorItem({ - required String errorMessage, - required VoidCallback onError, - }) : _errorMessage = errorMessage, - _onError = onError; - - final String _errorMessage; - final VoidCallback _onError; - - @override - Widget build(BuildContext context) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - onPressed: _onError, - icon: const Icon(Icons.refresh), - ), - Text(_errorMessage), - ], - ), - ); - } -} - -class _LoadingItem extends StatelessWidget { - const _LoadingItem(); - - @override - Widget build(BuildContext context) => - const Center(child: CircularProgressIndicator()); -} - -class _EndItemWhenError extends StatelessWidget { - const _EndItemWhenError({required void Function() onPressed}) - : _onPressed = onPressed; - final void Function() _onPressed; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 130, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const Flexible( - flex: 3, - child: Center( - child: Icon(Icons.cloud_off), - ), - ), - const Flexible( - flex: 2, - child: Center( - child: Text( - 'Please check your connection and try again.', - ), - ), - ), - Flexible( - flex: 3, - child: Center( - child: ElevatedButton( - style: ElevatedButton.styleFrom( - shape: const StadiumBorder(), - ), - onPressed: _onPressed, - child: const Text('refresh'), - ), - ), - ), - ], - ), - ); - } -} diff --git a/packages/cores/core/lib/ui.dart b/packages/cores/core/lib/ui.dart index bde014e2..0d2bd03b 100644 --- a/packages/cores/core/lib/ui.dart +++ b/packages/cores/core/lib/ui.dart @@ -1,2 +1 @@ -export 'src/ui/util/common_paging_view.dart'; export 'src/ui/util/snack_bar_manager.dart'; From f029f0b51cfd3e74e68c8d1b66872ae995c062b5 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Thu, 9 Jan 2025 09:03:04 +0900 Subject: [PATCH 07/16] refactor: Split exports into individual files. --- .../lib/src/ui/github_repository_list_page.dart | 2 +- .../lib/src/ui/provider/github_repository_list_notifier.dart | 3 ++- packages/utils/pagination/lib/model.dart | 1 + packages/utils/pagination/lib/pagination.dart | 3 --- packages/utils/pagination/lib/provider.dart | 1 + packages/utils/pagination/lib/src/ui/common_paging_view.dart | 3 ++- packages/utils/pagination/lib/ui.dart | 1 + 7 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 packages/utils/pagination/lib/model.dart delete mode 100644 packages/utils/pagination/lib/pagination.dart create mode 100644 packages/utils/pagination/lib/provider.dart create mode 100644 packages/utils/pagination/lib/ui.dart diff --git a/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart b/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart index 1b1eec2a..f9a6a802 100644 --- a/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart +++ b/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart @@ -7,7 +7,7 @@ import 'package:samples_github_repository/src/data/provider/scroll_notifier.dart import 'package:samples_github_repository/src/domain/model/github_repository.dart'; import 'package:samples_github_repository/src/ui/provider/github_repository_list_notifier.dart'; import 'package:samples_github_repository/ui.dart'; -import 'package:utils_pagination/pagination.dart'; +import 'package:utils_pagination/ui.dart'; abstract interface class GitHubRepositoryListPageNavigator { void goGitHubRepositoryDetailPage( diff --git a/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart b/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart index ac7c1881..d30ef88e 100644 --- a/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart +++ b/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart @@ -1,7 +1,8 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:samples_github_repository/src/data/provider/repository.dart'; import 'package:samples_github_repository/src/domain/model/github_repository.dart'; -import 'package:utils_pagination/pagination.dart'; +import 'package:utils_pagination/model.dart'; +import 'package:utils_pagination/provider.dart'; final pageBasedGitHubRepositoryNotifierProvider = AsyncNotifierProvider .autoDispose( diff --git a/packages/utils/pagination/lib/model.dart b/packages/utils/pagination/lib/model.dart new file mode 100644 index 00000000..6919721d --- /dev/null +++ b/packages/utils/pagination/lib/model.dart @@ -0,0 +1 @@ +export 'src/model/paging_data.dart'; diff --git a/packages/utils/pagination/lib/pagination.dart b/packages/utils/pagination/lib/pagination.dart deleted file mode 100644 index 636e706e..00000000 --- a/packages/utils/pagination/lib/pagination.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'src/model/paging_data.dart'; -export 'src/provider/paging_async_notifier.dart'; -export 'src/ui/common_paging_view.dart'; diff --git a/packages/utils/pagination/lib/provider.dart b/packages/utils/pagination/lib/provider.dart new file mode 100644 index 00000000..24722ab1 --- /dev/null +++ b/packages/utils/pagination/lib/provider.dart @@ -0,0 +1 @@ +export 'src/provider/paging_async_notifier.dart'; diff --git a/packages/utils/pagination/lib/src/ui/common_paging_view.dart b/packages/utils/pagination/lib/src/ui/common_paging_view.dart index 11718153..8054227a 100644 --- a/packages/utils/pagination/lib/src/ui/common_paging_view.dart +++ b/packages/utils/pagination/lib/src/ui/common_paging_view.dart @@ -2,7 +2,8 @@ import 'package:cores_core/exception.dart'; import 'package:cores_core/util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:utils_pagination/pagination.dart'; +import 'package:utils_pagination/model.dart'; +import 'package:utils_pagination/provider.dart'; import 'package:utils_pagination/src/extension/async_value.dart'; import 'package:visibility_detector/visibility_detector.dart'; diff --git a/packages/utils/pagination/lib/ui.dart b/packages/utils/pagination/lib/ui.dart new file mode 100644 index 00000000..b35fe506 --- /dev/null +++ b/packages/utils/pagination/lib/ui.dart @@ -0,0 +1 @@ +export 'src/ui/common_paging_view.dart'; From b39b26a6b8d0709691237e3836bfa55de7030d90 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Thu, 9 Jan 2025 15:46:44 +0900 Subject: [PATCH 08/16] chore(utils_pagination): Update gitignore. --- packages/utils/pagination/.gitignore | 5 +++++ packages/utils/pagination/pubspec_overrides.yaml | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) delete mode 100644 packages/utils/pagination/pubspec_overrides.yaml diff --git a/packages/utils/pagination/.gitignore b/packages/utils/pagination/.gitignore index 47c816ae..43dba567 100644 --- a/packages/utils/pagination/.gitignore +++ b/packages/utils/pagination/.gitignore @@ -25,3 +25,8 @@ migrate_working_dir/ **/doc/api/ .dart_tool/ build/ +.flutter-plugins +.flutter-plugins-dependencies + +# Melos related +/pubspec_overrides.yaml diff --git a/packages/utils/pagination/pubspec_overrides.yaml b/packages/utils/pagination/pubspec_overrides.yaml deleted file mode 100644 index ae07ac2f..00000000 --- a/packages/utils/pagination/pubspec_overrides.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# melos_managed_dependency_overrides: cores_core -dependency_overrides: - cores_core: - path: ../../cores/core From 67f439e8651da5730abf07b8f9b451d578f65ac0 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Thu, 9 Jan 2025 15:54:48 +0900 Subject: [PATCH 09/16] chore: Update flutter version of yumemi_lints for consistency. --- packages/utils/pagination/analysis_options.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/pagination/analysis_options.yaml b/packages/utils/pagination/analysis_options.yaml index a81b2133..5af95b85 100644 --- a/packages/utils/pagination/analysis_options.yaml +++ b/packages/utils/pagination/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:yumemi_lints/flutter/3.19/recommended.yaml +include: package:yumemi_lints/flutter/3.24/recommended.yaml analyzer: plugins: From 922e8c251aaf9c81ae1c05bca73c14bfb5b2b9a2 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Thu, 9 Jan 2025 15:55:26 +0900 Subject: [PATCH 10/16] chore: Remove unnecessary whitespace. --- packages/utils/pagination/analysis_options.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/utils/pagination/analysis_options.yaml b/packages/utils/pagination/analysis_options.yaml index 5af95b85..e38e425f 100644 --- a/packages/utils/pagination/analysis_options.yaml +++ b/packages/utils/pagination/analysis_options.yaml @@ -4,4 +4,3 @@ analyzer: plugins: # https://riverpod.dev/docs/introduction/getting_started#enabling-riverpod_lintcustom_lint - custom_lint - \ No newline at end of file From 8c30721cbf354002361b4c3a10223be1aa11f206 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Thu, 9 Jan 2025 16:32:43 +0900 Subject: [PATCH 11/16] chore: Run melos bs. --- packages/utils/pagination/pubspec.lock | 96 +++++++++++++------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/packages/utils/pagination/pubspec.lock b/packages/utils/pagination/pubspec.lock index c56ab307..d48f8e1d 100644 --- a/packages/utils/pagination/pubspec.lock +++ b/packages/utils/pagination/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + sha256: "88399e291da5f7e889359681a8f64b18c5123e03576b01f32a6a276611e511c3" url: "https://pub.dev" source: hosted - version: "76.0.0" + version: "78.0.0" _macros: dependency: transitive description: dart @@ -18,18 +18,18 @@ packages: dependency: transitive description: name: analyzer - sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + sha256: "62899ef43d0b962b056ed2ebac6b47ec76ffd003d5f7c4e4dc870afe63188e33" url: "https://pub.dev" source: hosted - version: "6.11.0" + version: "7.1.0" analyzer_plugin: dependency: transitive description: name: analyzer_plugin - sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" + sha256: "1d460d14e3c2ae36dc2b32cef847c4479198cf87704f63c3c3c8150ee50c3916" url: "https://pub.dev" source: hosted - version: "0.11.3" + version: "0.12.0" args: dependency: transitive description: @@ -42,18 +42,18 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" build: dependency: transitive description: @@ -193,42 +193,42 @@ packages: dependency: "direct dev" description: name: custom_lint - sha256: "3486c470bb93313a9417f926c7dd694a2e349220992d7b9d14534dc49c15bba9" + sha256: "6d509673c4dd0baa90e60dc8366bc2acc6690f16a7d44bfae31294d82c5d2a62" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.1" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: "42cdc41994eeeddab0d7a722c7093ec52bd0761921eeb2cbdbf33d192a234759" + sha256: "8cc525c7b160eb47bb1ded8b2633c0f8b907930eb986ac577aded87cdd2835fe" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.1" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: "02450c3e45e2a6e8b26c4d16687596ab3c4644dd5792e3313aa9ceba5a49b7f5" + sha256: "6dcee8a017181941c51a110da7e267c1d104dc74bec8862eeb8c85b5c8759a9e" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.1" custom_lint_visitor: dependency: transitive description: name: custom_lint_visitor - sha256: bfe9b7a09c4775a587b58d10ebb871d4fe618237639b1e84d5ec62d7dfef25f9 + sha256: "14df0760dfa81b7b0c398c876045f4e4a343eb2c9d200c66163671dd3e337c1b" url: "https://pub.dev" source: hosted - version: "1.0.0+6.11.0" + version: "1.0.0+7.1.0" dart_style: dependency: transitive description: name: dart_style - sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" + sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" url: "https://pub.dev" source: hosted - version: "2.3.7" + version: "3.0.1" file: dependency: transitive description: @@ -262,10 +262,10 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "44c19278dd9d89292cf46e97dc0c1e52ce03275f40a97c5a348e802a924bf40e" + sha256: "59a584c24b3acdc5250bb856d0d3e9c0b798ed14a4af1ddb7dc1c7b41df91c9c" url: "https://pub.dev" source: hosted - version: "2.5.7" + version: "2.5.8" freezed_annotation: dependency: "direct main" description: @@ -366,10 +366,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -406,10 +406,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" pool: dependency: transitive description: @@ -430,10 +430,10 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0" + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" riverpod: dependency: transitive description: @@ -446,10 +446,10 @@ packages: dependency: transitive description: name: riverpod_analyzer_utils - sha256: c6b8222b2b483cb87ae77ad147d6408f400c64f060df7a225b127f4afef4f8c8 + sha256: "837a6dc33f490706c7f4632c516bcd10804ee4d9ccc8046124ca56388715fdf3" url: "https://pub.dev" source: hosted - version: "0.5.8" + version: "0.5.9" riverpod_annotation: dependency: transitive description: @@ -462,10 +462,10 @@ packages: dependency: "direct dev" description: name: riverpod_lint - sha256: "83e4caa337a9840469b7b9bd8c2351ce85abad80f570d84146911b32086fbd99" + sha256: b05408412b0f75dec954e032c855bc28349eeed2d2187f94519e1ddfdf8b3693 url: "https://pub.dev" source: hosted - version: "2.6.3" + version: "2.6.4" rxdart: dependency: transitive description: @@ -507,18 +507,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "2.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" sprintf: dependency: transitive description: @@ -531,10 +531,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" state_notifier: dependency: transitive description: @@ -547,10 +547,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" stream_transform: dependency: transitive description: @@ -563,26 +563,26 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.4" timing: dependency: transitive description: @@ -627,10 +627,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.3.1" watcher: dependency: transitive description: From 8a56e130d2b705a5245628e3e9ab2ec3d13c4eba Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Thu, 9 Jan 2025 16:55:31 +0900 Subject: [PATCH 12/16] chore: Run melos bs. --- packages/utils/pagination/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/utils/pagination/pubspec.yaml b/packages/utils/pagination/pubspec.yaml index ecf1071d..5696c1a9 100644 --- a/packages/utils/pagination/pubspec.yaml +++ b/packages/utils/pagination/pubspec.yaml @@ -17,8 +17,8 @@ dependencies: visibility_detector: ^0.4.0+2 dev_dependencies: - build_runner: ^2.4.11 + build_runner: ^2.4.14 custom_lint: ^0.7.0 - freezed: ^2.5.2 + freezed: ^2.5.7 riverpod_lint: ^2.3.10 yumemi_lints: ^2.2.0 From e9ef728ccd7bedb624877b3393aa9cb8ed3382b3 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Fri, 10 Jan 2025 10:36:46 +0900 Subject: [PATCH 13/16] chore(utils_pagination): Remove logging process. --- .../utils/pagination/lib/src/ui/common_paging_view.dart | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/utils/pagination/lib/src/ui/common_paging_view.dart b/packages/utils/pagination/lib/src/ui/common_paging_view.dart index 8054227a..a46de5aa 100644 --- a/packages/utils/pagination/lib/src/ui/common_paging_view.dart +++ b/packages/utils/pagination/lib/src/ui/common_paging_view.dart @@ -1,5 +1,4 @@ import 'package:cores_core/exception.dart'; -import 'package:cores_core/util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:utils_pagination/model.dart'; @@ -100,13 +99,6 @@ class CommonPagingView, _onError(error); return; } - // AppException is expected, so this should never be reached. - logger.shout( - 'Unexpected error type encountered: $error - ' - 'This indicates a need to revise exception handling to ensure only ' - 'AppException is thrown. Please review exception handling ' - 'practices and modify as necessary.', - ); } }, ); From e99b53a960293dcd988fffbdfa9a8d3e590a4747 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Wed, 15 Jan 2025 22:09:50 +0900 Subject: [PATCH 14/16] feat(paging_utils): Define PagingException. --- packages/utils/pagination/lib/exception.dart | 1 + .../lib/src/exception/paging_exception.dart | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 packages/utils/pagination/lib/exception.dart create mode 100644 packages/utils/pagination/lib/src/exception/paging_exception.dart diff --git a/packages/utils/pagination/lib/exception.dart b/packages/utils/pagination/lib/exception.dart new file mode 100644 index 00000000..e18e05eb --- /dev/null +++ b/packages/utils/pagination/lib/exception.dart @@ -0,0 +1 @@ +export 'src/exception/paging_exception.dart'; diff --git a/packages/utils/pagination/lib/src/exception/paging_exception.dart b/packages/utils/pagination/lib/src/exception/paging_exception.dart new file mode 100644 index 00000000..c1298c1a --- /dev/null +++ b/packages/utils/pagination/lib/src/exception/paging_exception.dart @@ -0,0 +1,26 @@ +sealed class PagingException implements Exception { + const PagingException(this.message); + + final String message; +} + +sealed class PagingNetworkException extends PagingException { + const PagingNetworkException(super.message); +} + +final class PagingClientNetworkException extends PagingNetworkException { + const PagingClientNetworkException(super.message); +} + +final class PagingServerNetworkException extends PagingNetworkException { + const PagingServerNetworkException(super.message); +} + +final class PagingUnknownNetworkException extends PagingNetworkException { + const PagingUnknownNetworkException() + : super('Unknown network error occurred'); +} + +final class PagingUnknownException extends PagingException { + const PagingUnknownException() : super('Unknown error occurred'); +} From 3d11ae31cd0e52cb93f89b1eeb723be4efad53f3 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Wed, 15 Jan 2025 22:21:18 +0900 Subject: [PATCH 15/16] refactor: Replace AppException to PagingException in CommonPagingView. --- .../src/ui/github_repository_list_page.dart | 20 +++++++++++-- .../github_repository_list_notifier.dart | 29 ++++++++++++++----- .../lib/src/ui/common_paging_view.dart | 8 ++--- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart b/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart index f9a6a802..511c1910 100644 --- a/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart +++ b/packages/samples/github_repository/lib/src/ui/github_repository_list_page.dart @@ -1,4 +1,5 @@ -import 'package:cores_core/exception.dart'; +import 'dart:async'; + import 'package:cores_designsystem/components.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -61,7 +62,22 @@ class GitHubRepositoryListPage extends HookConsumerWidget { ); }, ), - onError: ref.read(appExceptionNotifierProvider.notifier).notify, + onError: (e) => unawaited( + showDialog( + context: context, + builder: (_) { + return AlertDialog( + title: Text(e.message), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Close'), + ), + ], + ); + }, + ), + ), ); } } diff --git a/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart b/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart index d30ef88e..bdb1ad11 100644 --- a/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart +++ b/packages/samples/github_repository/lib/src/ui/provider/github_repository_list_notifier.dart @@ -1,6 +1,8 @@ +import 'package:cores_core/exception.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:samples_github_repository/src/data/provider/repository.dart'; import 'package:samples_github_repository/src/domain/model/github_repository.dart'; +import 'package:utils_pagination/exception.dart'; import 'package:utils_pagination/model.dart'; import 'package:utils_pagination/provider.dart'; @@ -17,13 +19,26 @@ class PageBasedGitHubRepositoryNotifier Future> fetch({ required int page, }) async { - final res = - await ref.read(listOrganizationRepositoriesProvider(page: page).future); - ref.keepAlive(); - return PageBasedFetchResult( - items: res.items, - hasMore: res.hasMore, - ); + try { + final res = await ref + .read(listOrganizationRepositoriesProvider(page: page).future); + ref.keepAlive(); + return PageBasedFetchResult( + items: res.items, + hasMore: res.hasMore, + ); + } on AppException catch (e) { + switch (e) { + case ClientNetworkException(): + throw PagingClientNetworkException(e.message); + case ServerNetworkException(): + throw PagingServerNetworkException(e.message); + case UnknownNetworkException(): + throw const PagingUnknownNetworkException(); + case UnknownException(): + throw const PagingUnknownException(); + } + } } @override diff --git a/packages/utils/pagination/lib/src/ui/common_paging_view.dart b/packages/utils/pagination/lib/src/ui/common_paging_view.dart index a46de5aa..0e947b38 100644 --- a/packages/utils/pagination/lib/src/ui/common_paging_view.dart +++ b/packages/utils/pagination/lib/src/ui/common_paging_view.dart @@ -1,8 +1,8 @@ -import 'package:cores_core/exception.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:utils_pagination/model.dart'; import 'package:utils_pagination/provider.dart'; +import 'package:utils_pagination/src/exception/paging_exception.dart'; import 'package:utils_pagination/src/extension/async_value.dart'; import 'package:visibility_detector/visibility_detector.dart'; @@ -45,7 +45,7 @@ class CommonPagingView, const CommonPagingView({ required AutoDisposeAsyncNotifierProvider provider, required Widget Function(D data, Widget? endItem) contentBuilder, - required void Function(AppException e) onError, + required void Function(PagingException e) onError, super.key, }) : _contentBuilder = contentBuilder, _provider = provider, @@ -61,7 +61,7 @@ class CommonPagingView, /// adding a special widget at that position. final Widget Function(D data, Widget? endItem) _contentBuilder; - final void Function(AppException e) _onError; + final void Function(PagingException e) _onError; Widget? _endItem( D data, @@ -95,7 +95,7 @@ class CommonPagingView, (_, next) { if (!next.isLoading && next.hasError) { final error = next.error; - if (error is AppException) { + if (error is PagingException) { _onError(error); return; } From 1a0d479e3f038b21f08322ecd172fc38b79f8917 Mon Sep 17 00:00:00 2001 From: trm11tkr Date: Thu, 16 Jan 2025 10:53:08 +0900 Subject: [PATCH 16/16] chore: Remove cores_core from: utils_pagination. --- packages/utils/pagination/pubspec.lock | 23 ----------------------- packages/utils/pagination/pubspec.yaml | 2 -- 2 files changed, 25 deletions(-) diff --git a/packages/utils/pagination/pubspec.lock b/packages/utils/pagination/pubspec.lock index d48f8e1d..f812d0c0 100644 --- a/packages/utils/pagination/pubspec.lock +++ b/packages/utils/pagination/pubspec.lock @@ -174,13 +174,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" - cores_core: - dependency: "direct main" - description: - path: "../../cores/core" - relative: true - source: path - version: "0.0.1" crypto: dependency: transitive description: @@ -450,14 +443,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.9" - riverpod_annotation: - dependency: transitive - description: - name: riverpod_annotation - sha256: e14b0bf45b71326654e2705d462f21b958f987087be850afd60578fcd502d1b8 - url: "https://pub.dev" - source: hosted - version: "2.6.1" riverpod_lint: dependency: "direct dev" description: @@ -490,14 +475,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" - simple_logger: - dependency: transitive - description: - name: simple_logger - sha256: "1de79f22bf31e5c33b91e9e302394dac02d8269d474848d33153c3a15c08e970" - url: "https://pub.dev" - source: hosted - version: "1.10.0" sky_engine: dependency: transitive description: flutter diff --git a/packages/utils/pagination/pubspec.yaml b/packages/utils/pagination/pubspec.yaml index 5696c1a9..757c8084 100644 --- a/packages/utils/pagination/pubspec.yaml +++ b/packages/utils/pagination/pubspec.yaml @@ -8,8 +8,6 @@ environment: flutter: ^3.27.1 dependencies: - cores_core: - path: ../../cores/core flutter: sdk: flutter flutter_riverpod: ^2.6.1