Skip to content

Commit c77b287

Browse files
committed
usable
1 parent 27046de commit c77b287

15 files changed

+516
-142
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
2. subKey : params
88
2. Flags :
99
1. end time
10-
2. UseExpiredDataIfNetError
1110
3. Remove by:
1211
1. key
1312
2. key + subKey

example/lib/main.dart

Lines changed: 19 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
1+
import 'dart:convert';
2+
3+
import 'package:dio/dio.dart';
4+
import 'package:dio_cache/dio_cache.dart';
15
import 'package:flutter/material.dart';
26

37
void main() => runApp(MyApp());
48

59
class MyApp extends StatelessWidget {
6-
// This widget is the root of your application.
710
@override
811
Widget build(BuildContext context) {
912
return MaterialApp(
1013
title: 'Flutter Demo',
1114
theme: ThemeData(
12-
// This is the theme of your application.
13-
//
14-
// Try running your application with "flutter run". You'll see the
15-
// application has a blue toolbar. Then, without quitting the app, try
16-
// changing the primarySwatch below to Colors.green and then invoke
17-
// "hot reload" (press "r" in the console where you ran "flutter run",
18-
// or simply save your changes to "hot reload" in a Flutter IDE).
19-
// Notice that the counter didn't reset back to zero; the application
20-
// is not restarted.
2115
primarySwatch: Colors.blue,
2216
),
2317
home: MyHomePage(title: 'Flutter Demo Home Page'),
@@ -28,81 +22,42 @@ class MyApp extends StatelessWidget {
2822
class MyHomePage extends StatefulWidget {
2923
MyHomePage({Key key, this.title}) : super(key: key);
3024

31-
// This widget is the home page of your application. It is stateful, meaning
32-
// that it has a State object (defined below) that contains fields that affect
33-
// how it looks.
34-
35-
// This class is the configuration for the state. It holds the values (in this
36-
// case the title) provided by the parent (in this case the App widget) and
37-
// used by the build method of the State. Fields in a Widget subclass are
38-
// always marked "final".
39-
4025
final String title;
4126

4227
@override
4328
_MyHomePageState createState() => _MyHomePageState();
4429
}
4530

4631
class _MyHomePageState extends State<MyHomePage> {
47-
int _counter = 0;
32+
String _content = "Hello";
33+
var _url = "https://5d241383e39785001406ead9.mockapi.io/user";
34+
static DioCacheManager _manager = DioCacheManager(CacheConfig());
35+
var _dio = Dio()..interceptors.add(_manager.interceptor);
4836

49-
void _incrementCounter() {
50-
setState(() {
51-
// This call to setState tells the Flutter framework that something has
52-
// changed in this State, which causes it to rerun the build method below
53-
// so that the display can reflect the updated values. If we changed
54-
// _counter without calling setState(), then the build method would not be
55-
// called again, and so nothing would appear to happen.
56-
_counter++;
37+
void _doPost() {
38+
_dio.post(_url, options: buildCacheOptions(Duration(hours: 1), subKey: "id=1")).then((response) {
39+
setState(() {
40+
_content = jsonEncode(response.data);
41+
});
5742
});
5843
}
5944

6045
@override
6146
Widget build(BuildContext context) {
62-
// This method is rerun every time setState is called, for instance as done
63-
// by the _incrementCounter method above.
64-
//
65-
// The Flutter framework has been optimized to make rerunning build methods
66-
// fast, so that you can just rebuild anything that needs updating rather
67-
// than having to individually change instances of widgets.
6847
return Scaffold(
6948
appBar: AppBar(
70-
// Here we take the value from the MyHomePage object that was created by
71-
// the App.build method, and use it to set our appbar title.
7249
title: Text(widget.title),
7350
),
7451
body: Center(
75-
// Center is a layout widget. It takes a single child and positions it
76-
// in the middle of the parent.
77-
child: Column(
78-
// Column is also layout widget. It takes a list of children and
79-
// arranges them vertically. By default, it sizes itself to fit its
80-
// children horizontally, and tries to be as tall as its parent.
81-
//
82-
// Invoke "debug painting" (press "p" in the console, choose the
83-
// "Toggle Debug Paint" action from the Flutter Inspector in Android
84-
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
85-
// to see the wireframe for each widget.
86-
//
87-
// Column has various properties to control how it sizes itself and
88-
// how it positions its children. Here we use mainAxisAlignment to
89-
// center the children vertically; the main axis here is the vertical
90-
// axis because Columns are vertical (the cross axis would be
91-
// horizontal).
92-
mainAxisAlignment: MainAxisAlignment.center,
93-
children: <Widget>[
94-
Text(
95-
'You have pushed the button this many times:',
96-
),
97-
Text(
98-
'$_counter',
99-
style: Theme.of(context).textTheme.display1,
100-
),
101-
],
52+
child: SingleChildScrollView(
53+
child: Text(
54+
'$_content',
55+
style: Theme.of(context).textTheme.display1,
56+
),
10257
),
10358
),
10459
floatingActionButton: FloatingActionButton(
105-
onPressed: _incrementCounter,
60+
onPressed: _doPost,
10661
tooltip: 'Increment',
10762
child: Icon(Icons.add),
10863
), // This trailing comma makes auto-formatting nicer for build methods.

example/test/widget_test.dart

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
1-
// This is a basic Flutter widget test.
1+
//// This is a basic Flutter widget test.
2+
////
3+
//// To perform an interaction with a widget in your test, use the WidgetTester
4+
//// utility that Flutter provides. For example, you can send tap and scroll
5+
//// gestures. You can also use WidgetTester to find child widgets in the widget
6+
//// tree, read text, and verify that the values of widget properties are correct.
27
//
3-
// To perform an interaction with a widget in your test, use the WidgetTester
4-
// utility that Flutter provides. For example, you can send tap and scroll
5-
// gestures. You can also use WidgetTester to find child widgets in the widget
6-
// tree, read text, and verify that the values of widget properties are correct.
7-
8-
import 'package:flutter/material.dart';
9-
import 'package:flutter_test/flutter_test.dart';
10-
11-
import 'package:example/main.dart';
12-
13-
void main() {
14-
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15-
// Build our app and trigger a frame.
16-
await tester.pumpWidget(MyApp());
17-
18-
// Verify that our counter starts at 0.
19-
expect(find.text('0'), findsOneWidget);
20-
expect(find.text('1'), findsNothing);
21-
22-
// Tap the '+' icon and trigger a frame.
23-
await tester.tap(find.byIcon(Icons.add));
24-
await tester.pump();
25-
26-
// Verify that our counter has incremented.
27-
expect(find.text('0'), findsNothing);
28-
expect(find.text('1'), findsOneWidget);
29-
});
30-
}
8+
//import 'package:flutter/material.dart';
9+
//import 'package:flutter_test/flutter_test.dart';
10+
//
11+
//import 'package:example/main.dart';
12+
//
13+
//void main() {
14+
// testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15+
// // Build our app and trigger a frame.
16+
// await tester.pumpWidget(MyApp());
17+
//
18+
// // Verify that our counter starts at 0.
19+
// expect(find.text('0'), findsOneWidget);
20+
// expect(find.text('1'), findsNothing);
21+
//
22+
// // Tap the '+' icon and trigger a frame.
23+
// await tester.tap(find.byIcon(Icons.add));
24+
// await tester.pump();
25+
//
26+
// // Verify that our counter has incremented.
27+
// expect(find.text('0'), findsNothing);
28+
// expect(find.text('1'), findsOneWidget);
29+
// });
30+
//}

lib/dio_cache.dart

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
library dio_cache;
22

3-
/// A Calculator.
4-
class Calculator {
5-
/// Returns [value] plus 1.
6-
int addOne(int value) => value + 1;
7-
}
3+
export 'src/builder_dio.dart';
4+
export 'src/core/config.dart';
5+
export 'src/manager_dio.dart';

lib/src/builder_dio.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import 'package:dio/dio.dart';
2+
import 'package:dio_cache/src/manager_dio.dart';
3+
4+
Options buildCacheOptions(Duration maxAge, {Options options, String subKey, Duration maxStale}) {
5+
if (null == options) options = Options();
6+
options.extra.addAll({DIO_CACHE_KEY_MAX_AGE: maxAge});
7+
if (null != subKey) options.extra.addAll({DIO_CACHE_KEY_SUB_KEY: subKey});
8+
if (null != maxStale) options.extra.addAll({DIO_CACHE_KEY_MAX_STALE: maxStale});
9+
return options;
10+
}

lib/src/core/config.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
typedef Future<String> Encrypt(String str);
2+
typedef Future<String> Decrypt(String str);
3+
4+
class CacheConfig {
5+
Duration defaultMaxAge;
6+
Duration defaultMaxStale;
7+
String databaseName;
8+
9+
bool skipMemoryCache;
10+
bool skipDiskCache;
11+
12+
int maxMemoryCacheCount;
13+
14+
Encrypt encrypt;
15+
Decrypt decrypt;
16+
17+
CacheConfig(
18+
{this.defaultMaxAge = const Duration(days: 7),
19+
this.defaultMaxStale,
20+
this.databaseName = "DioCache",
21+
this.skipDiskCache = false,
22+
this.skipMemoryCache = false,
23+
this.maxMemoryCacheCount = 50,
24+
this.encrypt,
25+
this.decrypt});
26+
}

lib/src/core/manager.dart

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import 'dart:convert';
2+
3+
import 'package:crypto/crypto.dart';
4+
import 'package:dio_cache/src/core/config.dart';
5+
import 'package:dio_cache/src/core/obj.dart';
6+
import 'package:dio_cache/src/core/store.dart';
7+
import 'package:sqflite/utils/utils.dart';
8+
9+
class CacheManager {
10+
CacheConfig _config;
11+
DiskCacheStore _diskCacheStore;
12+
MemoryCacheStore _memoryCacheStore;
13+
MD5 _md5;
14+
Utf8Encoder _utf8encoder;
15+
16+
CacheManager(this._config) {
17+
_md5 = md5;
18+
_utf8encoder = const Utf8Encoder();
19+
if (!_config.skipDiskCache) _diskCacheStore = DiskCacheStore(_config);
20+
if (!_config.skipMemoryCache) _memoryCacheStore = MemoryCacheStore(_config);
21+
}
22+
23+
Future<CacheObj> _pullFromCache(String key, {String subKey}) async {
24+
key = _convertMd5(key);
25+
if (null != subKey) subKey = _convertMd5(subKey);
26+
var obj = await _memoryCacheStore?.getCacheObj(key, subKey: subKey);
27+
if (null == obj) {
28+
obj = await _diskCacheStore?.getCacheObj(key, subKey: subKey);
29+
if (null != obj) _memoryCacheStore.setCacheObj(obj);
30+
}
31+
if (null != obj) {
32+
if (null != obj.maxStaleDate && obj.maxStaleDate > 0) {
33+
//if maxStaleDate exist, Remove maxStaleDate when it expires.
34+
if (obj.maxStaleDate < DateTime.now().millisecondsSinceEpoch) {
35+
delete(key, subKey: subKey);
36+
return null;
37+
}
38+
} else {
39+
//if maxStaleDate NOT exist, Remove maxAgeDate when it expires.
40+
if (obj.maxAgeDate < DateTime.now().millisecondsSinceEpoch) {
41+
delete(key, subKey: subKey);
42+
return null;
43+
}
44+
}
45+
}
46+
return obj;
47+
}
48+
49+
Future<String> pullFromCacheBeforeMaxAge(String key, {String subKey}) {
50+
return _pullFromCache(key, subKey: subKey).then((obj) {
51+
if (null != obj && null != obj.maxAgeDate && obj.maxAgeDate < DateTime.now().millisecondsSinceEpoch) {
52+
return null;
53+
}
54+
return obj?.content;
55+
});
56+
}
57+
58+
Future<String> pullFromCacheBeforeMaxStale(String key, {String subKey}) {
59+
return _pullFromCache(key, subKey: subKey).then((obj) {
60+
return obj?.content;
61+
});
62+
}
63+
64+
pushToCache(CacheObj obj) {
65+
obj.key = _convertMd5(obj.key);
66+
if (null != obj.subKey) obj.subKey = _convertMd5(obj.subKey);
67+
68+
if (null == obj.maxAgeDate || obj.maxAgeDate <= 0) {
69+
obj.maxAge = _config.defaultMaxAge;
70+
}
71+
if ((null == obj.maxStaleDate || obj.maxStaleDate <= 0) && null != _config.defaultMaxStale) {
72+
obj.maxStale = _config.defaultMaxStale;
73+
}
74+
_memoryCacheStore?.setCacheObj(obj);
75+
_diskCacheStore?.setCacheObj(obj);
76+
}
77+
78+
delete(String key, {String subKey}) {
79+
key = _convertMd5(key);
80+
if (null != subKey) subKey = _convertMd5(subKey);
81+
82+
_memoryCacheStore?.delete(key, subKey: subKey);
83+
_diskCacheStore?.delete(key, subKey: subKey);
84+
}
85+
86+
clearExpired() {
87+
_memoryCacheStore?.clearExpired();
88+
_diskCacheStore?.clearExpired();
89+
}
90+
91+
String _convertMd5(String str) {
92+
return hex(_md5.convert(_utf8encoder.convert(str)).bytes);
93+
}
94+
}

lib/src/core/obj.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'package:json_annotation/json_annotation.dart';
2+
3+
part 'obj.g.dart';
4+
5+
@JsonSerializable()
6+
class CacheObj {
7+
String key;
8+
String subKey;
9+
@JsonKey(name: "max_age_date")
10+
int maxAgeDate;
11+
@JsonKey(name: "max_stale_date")
12+
int maxStaleDate;
13+
String content;
14+
15+
CacheObj._(this.key, this.subKey, this.content);
16+
17+
factory CacheObj(String key, String content, {String subKey = "", Duration maxAge, Duration maxStale}) {
18+
return CacheObj._(key, subKey, content)
19+
..maxAge = maxAge
20+
..maxStale = maxStale;
21+
}
22+
23+
set maxAge(Duration duration) {
24+
if (null != duration) this.maxAgeDate = _convertDuration(duration);
25+
}
26+
27+
set maxStale(Duration duration) {
28+
if (null != duration) this.maxStaleDate = _convertDuration(duration);
29+
}
30+
31+
_convertDuration(Duration duration) => DateTime.now().add(duration).millisecondsSinceEpoch;
32+
33+
factory CacheObj.fromJson(Map<String, dynamic> json) => _$CacheObjFromJson(json);
34+
35+
toJson() => _$CacheObjToJson(this);
36+
}

lib/src/core/obj.g.dart

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)