-
Notifications
You must be signed in to change notification settings - Fork 6
load PFIs from known source and cache as shared preference #17
Changes from 1 commit
58863b8
14c8cb4
19f22cd
ab657e6
8afcd37
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,56 @@ | ||
import 'dart:convert'; | ||
import 'package:flutter_starter/features/pfis/pfi.dart'; | ||
import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||
import 'package:http/http.dart' as http; | ||
import 'package:shared_preferences/shared_preferences.dart'; | ||
|
||
final pfisProvider = Provider<List<Pfi>>( | ||
(ref) => [ | ||
Pfi( | ||
id: 'prototype', | ||
name: 'Prototype', | ||
didUri: 'did:dht:74hg1efatndi8enx3e4z6c4u8ieh1xfkyay4ntg4dg1w6risu35y', | ||
), | ||
Pfi( | ||
id: 'africa', | ||
name: 'Africa', | ||
didUri: 'coming soon...', | ||
), | ||
Pfi( | ||
id: 'mexico', | ||
name: 'Mexico', | ||
didUri: 'coming soon...', | ||
), | ||
], | ||
); | ||
final pfisProvider = FutureProvider<List<Pfi>>((ref) async { | ||
const url = 'https://raw.githubusercontent.com/TBD54566975/pfi-providers-data/main/pfis.json'; | ||
const cacheKey = 'pfis_cache'; | ||
|
||
try { | ||
// Attempt to fetch data from the URL | ||
final response = await http.get(Uri.parse(url)); | ||
if (response.statusCode == 200) { | ||
// Parse the JSON data directly into a list of Pfi objects | ||
List<Pfi> pfis = (json.decode(response.body) as List).map((data) { | ||
return Pfi( | ||
id: data['id'] as String, | ||
name: data['name'] as String, | ||
didUri: data['didUri'] as String, | ||
); | ||
}).toList(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the serialization change below, you can update to this if you wanna: final pfis = (json.decode(response.body) as List)
.map<Pfi>((p) => Pfi.fromJson(p))
.toList(); |
||
|
||
// Save the data to cache | ||
final prefs = await SharedPreferences.getInstance(); | ||
await prefs.setString(cacheKey, response.body); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can add a provider for this as well so that any code can use Then initialize and override it in Then when you need |
||
|
||
return pfis; | ||
} else { | ||
// If server returns an unsuccessful response, try loading from cache | ||
return await _loadFromCache(cacheKey); | ||
} | ||
} catch (e) { | ||
// In case of an error, try loading from cache | ||
return await _loadFromCache(cacheKey); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another approach I like to use on mobile, to keep things snappy is to always load cache first, then always load from network. That way the users gets the list right away from cache and we can refresh from network after. This would require another provider/notifier which is a bit more complicated. Happy to show how that might work as well if you're interested. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good idea - did that. |
||
}); | ||
|
||
Future<List<Pfi>> _loadFromCache(String cacheKey) async { | ||
final prefs = await SharedPreferences.getInstance(); | ||
String? cachedData = prefs.getString(cacheKey); | ||
|
||
if (cachedData != null) { | ||
// If cached data is available, parse and return it | ||
return (json.decode(cachedData) as List).map((data) { | ||
return Pfi( | ||
id: data['id'] as String, | ||
name: data['name'] as String, | ||
didUri: data['didUri'] as String, | ||
); | ||
}).toList(); | ||
Comment on lines
+43
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this type of stuff, you can add Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'didUri': didUri,
};
}
factory Pfi.fromJson(Map<String, dynamic> json) {
return Pfi(
id: json['id'],
name: json['name'],
didUri: json['didUri'],
);
} |
||
} else { | ||
// If there is no cached data, return an empty list or handle appropriately | ||
return []; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,32 +3,40 @@ import 'package:flutter_starter/features/pfis/pfi_providers.dart'; | |
import 'package:flutter_starter/features/pfis/pfi_verification_page.dart'; | ||
import 'package:flutter_starter/l10n/app_localizations.dart'; | ||
import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||
import 'package:flutter_starter/features/pfis/pfi.dart'; | ||
|
||
class PfisPage extends HookConsumerWidget { | ||
const PfisPage({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context, WidgetRef ref) { | ||
final pfis = ref.watch(pfisProvider); | ||
// Watch the provider and get the AsyncValue object | ||
AsyncValue<List<Pfi>> pfisAsyncValue = ref.watch(pfisProvider); | ||
|
||
return Scaffold( | ||
appBar: AppBar(title: Text(Loc.of(context).selectYourRegion)), | ||
body: ListView( | ||
children: [ | ||
...pfis.map( | ||
(pfi) => ListTile( | ||
title: Text(pfi.name), | ||
subtitle: Text(pfi.didUri), | ||
trailing: const Icon(Icons.chevron_right), | ||
onTap: () async { | ||
Navigator.of(context).push( | ||
MaterialPageRoute( | ||
builder: (_) => PfiVerificationPage(pfi: pfi), | ||
), | ||
); | ||
}, | ||
), | ||
) | ||
], | ||
body: pfisAsyncValue.when( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: but you can use the ref.watch(pfisProvider).when(.... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep - did it |
||
loading: () => const Center(child: CircularProgressIndicator()), | ||
error: (error, stack) => Center(child: Text('Error: $error')), | ||
data: (pfis) { | ||
// Build the list when data is available | ||
return ListView( | ||
children: pfis.map( | ||
(pfi) => ListTile( | ||
title: Text(pfi.name), | ||
subtitle: Text(pfi.didUri), | ||
trailing: const Icon(Icons.chevron_right), | ||
onTap: () async { | ||
Navigator.of(context).push( | ||
MaterialPageRoute( | ||
builder: (_) => PfiVerificationPage(pfi: pfi), | ||
), | ||
); | ||
}, | ||
), | ||
).toList(), | ||
); | ||
}, | ||
), | ||
); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super nit: We can make this private to the file to avoid having to pass it to the
_loadFromCache
function below.