forked from cfug/flutter.cn
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
137 changed files
with
5,750 additions
and
1,613 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# To learn more about how to use Nix to configure your environment | ||
# see: https://developers.google.com/idx/guides/customize-idx-env | ||
{ pkgs, ... }: { | ||
# Which nixpkgs channel to use. | ||
channel = "stable-24.05"; # or "unstable" | ||
|
||
# Use https://search.nixos.org/packages to find packages | ||
packages = [ | ||
pkgs.nodejs_22 | ||
pkgs.pnpm | ||
]; | ||
|
||
# Sets environment variables in the workspace | ||
env = {}; | ||
idx = { | ||
# Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" | ||
extensions = [ | ||
"Dart-Code.flutter" | ||
"Dart-Code.dart-code" | ||
]; | ||
|
||
# Enable previews | ||
previews = { | ||
enable = true; | ||
previews = { | ||
web = { | ||
command = ["./dash_site" "serve"]; | ||
manager = "web"; | ||
env = { | ||
# Environment variables to set for your server | ||
PORT = "$PORT"; | ||
}; | ||
}; | ||
}; | ||
}; | ||
|
||
# Workspace lifecycle hooks | ||
workspace = { | ||
# Runs when a workspace is first created | ||
onCreate = { | ||
get-submodule = "git submodule update --init --recursive"; | ||
pnpm-install = "pnpm install"; | ||
}; | ||
# Runs when the workspace is (re)started | ||
onStart = { | ||
# Example: start a background task to watch and re-build backend code | ||
# watch-backend = "npm run watch-backend"; | ||
}; | ||
}; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
examples/cookbook/architecture/offline_first/analysis_options.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Take our settings from the example_utils analysis_options.yaml file. | ||
# If necessary for a particular example, this file can also include | ||
# overrides for individual lints. | ||
|
||
include: package:example_utils/analysis.yaml |
149 changes: 149 additions & 0 deletions
149
...es/cookbook/architecture/offline_first/lib/data/repositories/user_profile_repository.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import 'dart:async'; | ||
|
||
import '../../domain/model/user_profile.dart'; | ||
import '../services/api_client_service.dart'; | ||
import '../services/database_service.dart'; | ||
|
||
class UserProfileRepository { | ||
UserProfileRepository({ | ||
required ApiClientService apiClientService, | ||
required DatabaseService databaseService, | ||
}) : _apiClientService = apiClientService, | ||
_databaseService = databaseService { | ||
// #docregion Timer | ||
Timer.periodic( | ||
const Duration(minutes: 5), | ||
(timer) => sync(), | ||
); | ||
// #enddocregion Timer | ||
} | ||
|
||
final ApiClientService _apiClientService; | ||
final DatabaseService _databaseService; | ||
|
||
// #docregion getUserProfile | ||
Stream<UserProfile> getUserProfile() async* { | ||
// Fetch the user profile from the database | ||
final userProfile = await _databaseService.fetchUserProfile(); | ||
// Returns the database result if it exists | ||
if (userProfile != null) { | ||
yield userProfile; | ||
} | ||
|
||
// Fetch the user profile from the API | ||
try { | ||
final apiUserProfile = await _apiClientService.getUserProfile(); | ||
//Update the database with the API result | ||
await _databaseService.updateUserProfile(apiUserProfile); | ||
// Return the API result | ||
yield apiUserProfile; | ||
} catch (e) { | ||
// Handle the error | ||
} | ||
} | ||
// #enddocregion getUserProfile | ||
|
||
// #docregion getUserProfileFallback | ||
Future<UserProfile> getUserProfileFallback() async { | ||
try { | ||
// Fetch the user profile from the API | ||
final apiUserProfile = await _apiClientService.getUserProfile(); | ||
//Update the database with the API result | ||
await _databaseService.updateUserProfile(apiUserProfile); | ||
|
||
return apiUserProfile; | ||
} catch (e) { | ||
// If the network call failed, | ||
// fetch the user profile from the database | ||
final databaseUserProfile = await _databaseService.fetchUserProfile(); | ||
|
||
// If the user profile was never fetched from the API | ||
// it will be null, so throw an error | ||
if (databaseUserProfile != null) { | ||
return databaseUserProfile; | ||
} else { | ||
// Handle the error | ||
throw Exception('User profile not found'); | ||
} | ||
} | ||
} | ||
// #enddocregion getUserProfileFallback | ||
|
||
// #docregion getUserProfileLocal | ||
Future<UserProfile> getUserProfileLocal() async { | ||
// Fetch the user profile from the database | ||
final userProfile = await _databaseService.fetchUserProfile(); | ||
|
||
// Return the database result if it exists | ||
if (userProfile == null) { | ||
throw Exception('Data not found'); | ||
} | ||
|
||
return userProfile; | ||
} | ||
|
||
Future<void> syncRead() async { | ||
try { | ||
// Fetch the user profile from the API | ||
final userProfile = await _apiClientService.getUserProfile(); | ||
|
||
// Update the database with the API result | ||
await _databaseService.updateUserProfile(userProfile); | ||
} catch (e) { | ||
// Try again later | ||
} | ||
} | ||
// #enddocregion getUserProfileLocal | ||
|
||
// #docregion updateUserProfileOnline | ||
Future<void> updateUserProfileOnline(UserProfile userProfile) async { | ||
try { | ||
// Update the API with the user profile | ||
await _apiClientService.putUserProfile(userProfile); | ||
|
||
// Only if the API call was successful | ||
// update the database with the user profile | ||
await _databaseService.updateUserProfile(userProfile); | ||
} catch (e) { | ||
// Handle the error | ||
} | ||
} | ||
// #enddocregion updateUserProfileOnline | ||
|
||
// #docregion updateUserProfileOffline | ||
Future<void> updateUserProfileOffline(UserProfile userProfile) async { | ||
// Update the database with the user profile | ||
await _databaseService.updateUserProfile(userProfile); | ||
|
||
try { | ||
// Update the API with the user profile | ||
await _apiClientService.putUserProfile(userProfile); | ||
} catch (e) { | ||
// Handle the error | ||
} | ||
} | ||
// #enddocregion updateUserProfileOffline | ||
|
||
// #docregion sync | ||
Future<void> sync() async { | ||
try { | ||
// Fetch the user profile from the database | ||
final userProfile = await _databaseService.fetchUserProfile(); | ||
|
||
// Check if the user profile requires synchronization | ||
if (userProfile == null || userProfile.synchronized) { | ||
return; | ||
} | ||
|
||
// Update the API with the user profile | ||
await _apiClientService.putUserProfile(userProfile); | ||
|
||
// Set the user profile as synchronized | ||
await _databaseService | ||
.updateUserProfile(userProfile.copyWith(synchronized: true)); | ||
} catch (e) { | ||
// Try again later | ||
} | ||
} | ||
// #enddocregion sync | ||
} |
26 changes: 26 additions & 0 deletions
26
examples/cookbook/architecture/offline_first/lib/data/services/api_client_service.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import '../../domain/model/user_profile.dart'; | ||
|
||
// #docregion ApiClientService | ||
class ApiClientService { | ||
/// performs GET network request to obtain a UserProfile | ||
Future<UserProfile> getUserProfile() async { | ||
// #enddocregion ApiClientService | ||
// Simulate a network GET request | ||
await Future.delayed(const Duration(seconds: 2)); | ||
// Return a dummy user profile | ||
return const UserProfile( | ||
name: 'John Doe (from API)', | ||
photoUrl: 'https://example.com/john_doe.jpg', | ||
); | ||
// #docregion ApiClientService | ||
} | ||
|
||
/// performs PUT network request to update a UserProfile | ||
Future<void> putUserProfile(UserProfile userProfile) async { | ||
// #enddocregion ApiClientService | ||
// Simulate a network PUT request | ||
await Future.delayed(const Duration(seconds: 2)); | ||
// #docregion ApiClientService | ||
} | ||
} | ||
// #enddocregion ApiClientService |
27 changes: 27 additions & 0 deletions
27
examples/cookbook/architecture/offline_first/lib/data/services/database_service.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import '../../domain/model/user_profile.dart'; | ||
|
||
// #docregion DatabaseService | ||
class DatabaseService { | ||
/// Fetches the UserProfile from the database. | ||
/// Returns null if the user profile is not found. | ||
Future<UserProfile?> fetchUserProfile() async { | ||
// #enddocregion DatabaseService | ||
// Simulate a database select query | ||
await Future.delayed(const Duration(milliseconds: 100)); | ||
// Return a dummy user profile | ||
return const UserProfile( | ||
name: 'John Doe (from Database)', | ||
photoUrl: 'https://example.com/john_doe.jpg', | ||
); | ||
// #docregion DatabaseService | ||
} | ||
|
||
/// Update UserProfile in the database. | ||
Future<void> updateUserProfile(UserProfile userProfile) async { | ||
// #enddocregion DatabaseService | ||
// Simulate a database update query | ||
await Future.delayed(const Duration(milliseconds: 100)); | ||
// #docregion DatabaseService | ||
} | ||
} | ||
// #enddocregion DatabaseService |
14 changes: 14 additions & 0 deletions
14
examples/cookbook/architecture/offline_first/lib/domain/model/user_profile.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
|
||
part 'user_profile.freezed.dart'; | ||
|
||
// #docregion UserProfile | ||
@freezed | ||
class UserProfile with _$UserProfile { | ||
const factory UserProfile({ | ||
required String name, | ||
required String photoUrl, | ||
@Default(false) bool synchronized, | ||
}) = _UserProfile; | ||
} | ||
// #enddocregion UserProfile |
Oops, something went wrong.