chore: update dependencies and remove unused files

- Replaced `flutter_highlight` with `gpt_markdown` in `pubspec.yaml`.
- Updated `pubspec.lock` to reflect new dependencies and removed obsolete ones.
- Deleted outdated Riverpod migration documentation files to streamline the project.
- Added new configurations for GptMarkdown styling in `markdown_config.dart` and updated the streaming markdown widget implementation.
This commit is contained in:
cogwheel0
2025-09-30 16:02:34 +05:30
parent ff6d33abdf
commit 12a6a07043
16 changed files with 149 additions and 5792 deletions

View File

@@ -1,474 +0,0 @@
# Riverpod Migration Example
## Example: Migrating SearchQueryNotifier
This example shows step-by-step how to migrate a simple provider from manual declaration to code generation.
---
## Current Code (Manual NotifierProvider)
**File:** `lib/core/providers/app_providers.dart` (lines ~1200-1209)
```dart
// Manual provider declaration
final searchQueryProvider = NotifierProvider<SearchQueryNotifier, String>(
SearchQueryNotifier.new,
);
class SearchQueryNotifier extends Notifier<String> {
@override
String build() => '';
void set(String query) => state = query;
}
```
**Usage in code:**
```dart
// Reading value
final query = ref.watch(searchQueryProvider);
// Updating value
ref.read(searchQueryProvider.notifier).set('new search');
```
---
## Migrated Code (Code Generation)
**File:** `lib/core/providers/app_providers.dart`
### Step 1: Add annotation and extend generated class
```dart
@riverpod
class SearchQuery extends _$SearchQuery { // Note: Class name changes
@override
String build() => '';
void set(String query) => state = query;
}
```
### Step 2: Run build_runner
```bash
dart run build_runner build --delete-conflicting-outputs
```
This generates `app_providers.g.dart` with:
```dart
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$searchQueryHash() => r'...';
/// See also [SearchQuery].
@ProviderFor(SearchQuery)
final searchQueryProvider = AutoDisposeNotifierProvider<SearchQuery, String>.internal(
SearchQuery.new,
name: r'searchQueryProvider',
debugGetCreateSourceHash: _$searchQueryHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$SearchQuery = AutoDisposeNotifier<String>;
```
### Step 3: Update imports (if needed)
No changes needed! The provider name stays the same: `searchQueryProvider`
### Step 4: Usage remains identical
```dart
// Reading value - NO CHANGE
final query = ref.watch(searchQueryProvider);
// Updating value - NO CHANGE
ref.read(searchQueryProvider.notifier).set('new search');
```
---
## Benefits of Migration
### Before (Manual)
```dart
// 8 lines of boilerplate
final searchQueryProvider = NotifierProvider<SearchQueryNotifier, String>(
SearchQueryNotifier.new,
);
class SearchQueryNotifier extends Notifier<String> {
@override
String build() => '';
void set(String query) => state = query;
}
```
**Issues:**
- ❌ More verbose
- ❌ Need to manually create provider variable
- ❌ Easy to forget to update provider declaration when class changes
- ❌ No automatic dependency tracking
### After (Code Generation)
```dart
// 6 lines, cleaner
@riverpod
class SearchQuery extends _$SearchQuery {
@override
String build() => '';
void set(String query) => state = query;
}
```
**Benefits:**
- ✅ Less boilerplate
- ✅ Provider auto-generated
- ✅ Type-safe
- ✅ Better IDE support
- ✅ Automatic dependency tracking
- ✅ Easier to add `family` or modifiers later
---
## More Complex Example: ThemeModeNotifier
### Current Code (Manual)
```dart
final themeModeProvider = NotifierProvider<ThemeModeNotifier, ThemeMode>(
ThemeModeNotifier.new,
);
class ThemeModeNotifier extends Notifier<ThemeMode> {
late final OptimizedStorageService _storage;
@override
ThemeMode build() {
_storage = ref.watch(optimizedStorageServiceProvider);
final storedMode = _storage.getThemeMode();
if (storedMode != null) {
return ThemeMode.values.firstWhere(
(e) => e.toString() == storedMode,
orElse: () => ThemeMode.system,
);
}
return ThemeMode.system;
}
void setTheme(ThemeMode mode) {
state = mode;
_storage.setThemeMode(mode.toString());
}
}
```
### Migrated Code (Code Generation)
```dart
@riverpod
class AppThemeMode extends _$AppThemeMode { // Renamed to avoid conflict with ThemeMode enum
late final OptimizedStorageService _storage;
@override
ThemeMode build() {
_storage = ref.watch(optimizedStorageServiceProvider);
final storedMode = _storage.getThemeMode();
if (storedMode != null) {
return ThemeMode.values.firstWhere(
(e) => e.toString() == storedMode,
orElse: () => ThemeMode.system,
);
}
return ThemeMode.system;
}
void setTheme(ThemeMode mode) {
state = mode;
_storage.setThemeMode(mode.toString());
}
}
// Generated provider will be: appThemeModeProvider
```
**Important:** Class renamed from `ThemeModeNotifier` to `AppThemeMode` to avoid name conflict with the `ThemeMode` enum from Flutter.
### Update Usage
```dart
// Before
final mode = ref.watch(themeModeProvider);
ref.read(themeModeProvider.notifier).setTheme(ThemeMode.dark);
// After
final mode = ref.watch(appThemeModeProvider);
ref.read(appThemeModeProvider.notifier).setTheme(ThemeMode.dark);
```
**Migration tool can help:**
```bash
# Find all usages
grep -r "themeModeProvider" lib/
# Replace with IDE refactoring or:
find lib -type f -name "*.dart" -exec sed -i '' 's/themeModeProvider/appThemeModeProvider/g' {} +
```
---
## Provider Function Example
### FutureProvider to @riverpod function
**Before:**
```dart
final serverConfigsProvider = FutureProvider<List<ServerConfig>>((ref) async {
final storage = ref.watch(optimizedStorageServiceProvider);
return storage.getServerConfigs();
});
```
**After:**
```dart
@riverpod
Future<List<ServerConfig>> serverConfigs(ServerConfigsRef ref) async {
final storage = ref.watch(optimizedStorageServiceProvider);
return storage.getServerConfigs();
}
// Generated provider name: serverConfigsProvider (same!)
```
**Usage - NO CHANGE:**
```dart
final configs = ref.watch(serverConfigsProvider);
// or
final configs = await ref.read(serverConfigsProvider.future);
```
---
## Family Provider Example
### Before (Manual)
```dart
final loadConversationProvider = FutureProvider.family<Conversation, String>((
ref,
conversationId,
) async {
final api = ref.watch(apiServiceProvider);
if (api == null) {
throw Exception('No API service available');
}
return await api.getConversation(conversationId);
});
```
### After (Code Generation)
```dart
@riverpod
Future<Conversation> loadConversation(
LoadConversationRef ref,
String conversationId, // Family parameter
) async {
final api = ref.watch(apiServiceProvider);
if (api == null) {
throw Exception('No API service available');
}
return await api.getConversation(conversationId);
}
// Usage stays the same!
// ref.watch(loadConversationProvider(conversationId))
```
**Benefits:**
- ✅ Automatic `.family` modifier handling
- ✅ Type-safe parameters
- ✅ Better parameter completion in IDE
- ✅ Can add multiple parameters easily
---
## Keep Alive Example
### Before
```dart
@Riverpod(keepAlive: true)
class AuthStateManager extends _$AuthStateManager {
// ...
}
```
### After
**No change needed!** Already using code generation correctly. ✅
---
## Migration Checklist
For each provider to migrate:
- [ ] Identify the provider type (Notifier, AsyncNotifier, function)
- [ ] Check for name conflicts (e.g., `ThemeModeNotifier` vs `ThemeMode`)
- [ ] Add `@riverpod` annotation
- [ ] Change class to extend `_$ClassName`
- [ ] Remove manual provider declaration
- [ ] Run `dart run build_runner build`
- [ ] Update all usages (IDE refactoring recommended)
- [ ] Test the provider functionality
- [ ] Commit the change
---
## Testing After Migration
### Unit Test Example
**Before:**
```dart
test('searchQuery updates correctly', () {
final container = ProviderContainer();
expect(container.read(searchQueryProvider), '');
container.read(searchQueryProvider.notifier).set('test');
expect(container.read(searchQueryProvider), 'test');
});
```
**After:**
```dart
test('searchQuery updates correctly', () {
final container = ProviderContainer();
// Same test code - no changes needed!
expect(container.read(searchQueryProvider), '');
container.read(searchQueryProvider.notifier).set('test');
expect(container.read(searchQueryProvider), 'test');
});
```
Tests remain identical! ✅
---
## Common Pitfalls
### 1. Class Name Conflicts
**Problem:**
```dart
@riverpod
class ThemeMode extends _$ThemeMode { // ❌ Conflicts with Flutter's ThemeMode
// ...
}
```
**Solution:**
```dart
@riverpod
class AppThemeMode extends _$AppThemeMode { // ✅ Unique name
// ...
}
```
### 2. Forgetting to Run Build Runner
**Problem:** After adding `@riverpod`, code doesn't compile.
```
Error: The getter '_$SearchQuery' isn't defined for the class 'SearchQuery'.
```
**Solution:**
```bash
dart run build_runner build --delete-conflicting-outputs
```
### 3. Mixing Manual and Generated Providers
**Problem:** Some providers use `@riverpod`, others use manual `NotifierProvider`.
**Solution:** Be consistent! Migrate all providers in a file together to maintain consistency.
---
## IDE Support
### VS Code
Add to `.vscode/tasks.json`:
```json
{
"version": "2.0.0",
"tasks": [
{
"label": "build_runner watch",
"type": "shell",
"command": "dart run build_runner watch --delete-conflicting-outputs",
"isBackground": true,
"problemMatcher": []
}
]
}
```
Run with `Cmd+Shift+P` → "Tasks: Run Task" → "build_runner watch"
### Android Studio / IntelliJ
1. Run → Edit Configurations
2. Add new "Shell Script" configuration
3. Script text: `dart run build_runner watch --delete-conflicting-outputs`
4. Working directory: `$ProjectFileDir$`
---
## Summary
**Effort per provider:** ~5-10 minutes
**Risk level:** 🟢 Low (tests verify behavior)
**Benefit:** High (consistency, maintainability, developer experience)
**Recommended order:**
1. Start with simple `Notifier` classes (like `SearchQueryNotifier`)
2. Move to `FutureProvider` functions
3. Then tackle complex `AsyncNotifier` classes
4. Keep `@Riverpod(keepAlive: true)` providers for last (already correct)
**Total providers to migrate:** ~30-40 (based on codebase analysis)
**Estimated total time:** 5-8 hours spread across multiple sessions