Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class DemoPage extends StatelessWidget {
title: new Text("Offline Demo"),
),
body: OfflineBuilder(
checkHost : true, // check if internet is avaiable by a DNS request , the default value is false
// hostToCheck : "my.domaine.name", // the default value is google.com
connectivityBuilder: (
BuildContext context,
ConnectivityResult connectivity,
Expand Down
9 changes: 2 additions & 7 deletions example/lib/demo_page.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
import 'package:flutter/material.dart';

class DemoPage extends StatelessWidget {
const DemoPage({
Key key,
@required this.child,
}) : super(key: key);
const DemoPage({Key key, @required this.child}) : super(key: key);

final Widget child;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Offline Demo"),
),
appBar: AppBar(title: Text("Offline Demo")),
body: child,
);
}
Expand Down
42 changes: 9 additions & 33 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,17 @@ class MyApp extends StatelessWidget {
return MaterialApp(
title: 'Offline Demo',
theme: ThemeData.dark(),
home: Builder(
builder: (BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text("Demo 1"),
onPressed: () {
navigate(context, Demo1());
},
),
RaisedButton(
child: Text("Demo 2"),
onPressed: () {
navigate(context, Demo2());
},
),
RaisedButton(
child: Text("Demo 3"),
onPressed: () {
navigate(context, Demo3());
},
),
],
);
},
home: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(child: Text("Demo 1"), onPressed: () => navigate(context, Demo1())),
RaisedButton(child: Text("Demo 2"), onPressed: () => navigate(context, Demo2())),
RaisedButton(child: Text("Demo 3"), onPressed: () => navigate(context, Demo3())),
],
),
);
}

void navigate(BuildContext context, Widget widget) {
Navigator.of(context).push<void>(
MaterialPageRoute<void>(
builder: (BuildContext context) => DemoPage(child: widget),
),
);
}
void navigate(BuildContext context, Widget widget) =>
Navigator.of(context).push<void>(MaterialPageRoute<void>(builder: (_) => DemoPage(child: widget)));
}
56 changes: 26 additions & 30 deletions example/lib/widgets/demo_1.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@ class Demo1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return OfflineBuilder(
connectivityBuilder: (
BuildContext context,
ConnectivityResult connectivity,
Widget child,
) {
final bool connected = connectivity != ConnectivityResult.none;
connectivityBuilder: (BuildContext context, ConnectivityResult connectivity, Widget child) {
final bool isConnected = connectivity != ConnectivityResult.none;
return Stack(
fit: StackFit.expand,
children: [
Expand All @@ -21,26 +17,10 @@ class Demo1 extends StatelessWidget {
right: 0.0,
child: AnimatedContainer(
duration: const Duration(milliseconds: 350),
color: connected ? Color(0xFF00EE44) : Color(0xFFEE4400),
color: isConnected ? Color(0xFF00EE44) : Color(0xFFEE4400),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 350),
child: connected
? Text('ONLINE')
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('OFFLINE'),
SizedBox(width: 8.0),
SizedBox(
width: 12.0,
height: 12.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
],
),
child: isConnected ? Text('ONLINE') : _OfflineWidget(),
),
),
),
Expand All @@ -50,14 +30,30 @@ class Demo1 extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'There are no bottons to push :)',
),
Text(
'Just turn off your internet.',
),
Text('There are no bottons to push :)'),
Text('Just turn off your internet.'),
],
),
);
}
}

class _OfflineWidget extends StatelessWidget {
const _OfflineWidget({Key key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('OFFLINE'),
SizedBox(width: 8.0),
SizedBox(
width: 12.0,
height: 12.0,
child: CircularProgressIndicator(strokeWidth: 2.0, valueColor: AlwaysStoppedAnimation<Color>(Colors.white)),
),
],
);
}
}
23 changes: 6 additions & 17 deletions example/lib/widgets/demo_2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,25 @@ class Demo2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return OfflineBuilder(
connectivityBuilder: (
BuildContext context,
ConnectivityResult connectivity,
Widget child,
) {
connectivityBuilder: (BuildContext context, ConnectivityResult connectivity, Widget child) {
if (connectivity == ConnectivityResult.none) {
return Container(
color: Colors.white,
child: Center(
child: Text(
"Oops, \n\nNow we are Offline!",
style: TextStyle(color: Colors.black),
),
child: Text("Oops, \n\nNow we are Offline!", style: TextStyle(color: Colors.black)),
),
);
} else {
return child;
}

return child;
},
builder: (BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'There are no bottons to push :)',
),
Text(
'Just turn off your internet.',
),
Text('There are no bottons to push :)'),
Text('Just turn off your internet.'),
],
),
);
Expand Down
24 changes: 6 additions & 18 deletions example/lib/widgets/demo_3.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,25 @@ class Demo3 extends StatelessWidget {
Widget build(BuildContext context) {
return OfflineBuilder(
debounceDuration: Duration.zero,
connectivityBuilder: (
BuildContext context,
ConnectivityResult connectivity,
Widget child,
) {
connectivityBuilder: (BuildContext context, ConnectivityResult connectivity, Widget child) {
if (connectivity == ConnectivityResult.none) {
return Container(
color: Colors.white70,
child: Center(
child: Text(
"Oops, \n\nWe experienced a Delayed Offline!",
style: TextStyle(color: Colors.black),
),
child: Text("Oops, \n\nWe experienced a Delayed Offline!", style: TextStyle(color: Colors.black)),
),
);
}

return child;
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'There are no bottons to push :)',
),
Text(
'Just turn off your internet.',
),
Text(
'This one has a bit of a delay.',
),
Text('There are no bottons to push :)'),
Text('Just turn off your internet.'),
Text('This one has a bit of a delay.'),
],
),
),
Expand Down
24 changes: 22 additions & 2 deletions lib/src/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_offline/src/utils.dart';

const kOfflineDebounceDuration = Duration(seconds: 3);
const kHostToCheck = 'google.com';

class OfflineBuilder extends StatefulWidget {
factory OfflineBuilder({
Key key,
@required ValueWidgetBuilder<ConnectivityResult> connectivityBuilder,
Duration debounceDuration = kOfflineDebounceDuration,
String hostToCheck = kHostToCheck,
bool checkHost = false,
WidgetBuilder builder,
Widget child,
WidgetBuilder errorBuilder,
Expand All @@ -20,6 +23,8 @@ class OfflineBuilder extends StatefulWidget {
connectivityBuilder: connectivityBuilder,
connectivityService: Connectivity(),
debounceDuration: debounceDuration,
hostToCheck: hostToCheck,
checkHost: checkHost,
builder: builder,
child: child,
errorBuilder: errorBuilder,
Expand All @@ -32,6 +37,8 @@ class OfflineBuilder extends StatefulWidget {
@required this.connectivityBuilder,
@required this.connectivityService,
this.debounceDuration = kOfflineDebounceDuration,
this.hostToCheck = kHostToCheck,
this.checkHost = false,
this.builder,
this.child,
this.errorBuilder,
Expand All @@ -40,6 +47,8 @@ class OfflineBuilder extends StatefulWidget {
assert(connectivityService != null, 'connectivityService cannot be null'),
assert(!(builder is WidgetBuilder && child is Widget) && !(builder == null && child == null),
'You should specify either a builder or a child'),
assert(hostToCheck != null, 'hostToCheck cannot be null'),
assert(checkHost != null, 'checkHost can only be true or false'),
super(key: key);

/// Override connectivity service used for testing
Expand All @@ -48,6 +57,12 @@ class OfflineBuilder extends StatefulWidget {
/// Debounce duration from epileptic network situations
final Duration debounceDuration;

/// Hostname to test internet connection
final String hostToCheck;

/// Decide if to use the hostname option
final bool checkHost;

/// Used for building the Offline and/or Online UI
final ValueWidgetBuilder<ConnectivityResult> connectivityBuilder;

Expand All @@ -72,8 +87,13 @@ class OfflineBuilderState extends State<OfflineBuilder> {
super.initState();

_connectivityStream = Stream.fromFuture(widget.connectivityService.checkConnectivity())
.asyncExpand((data) => widget.connectivityService.onConnectivityChanged.transform(startsWith(data)))
.transform(debounce(widget.debounceDuration));
.asyncExpand((data) => widget.connectivityService.onConnectivityChanged.transform(startsWith(data)));

if (widget.checkHost) {
_connectivityStream = _connectivityStream.transform(checkIfHostIsAvailble(widget.hostToCheck));
}

_connectivityStream = _connectivityStream.transform(debounce(widget.debounceDuration));
}

@override
Expand Down
20 changes: 20 additions & 0 deletions lib/src/utils.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';

import 'package:connectivity/connectivity.dart';

Expand Down Expand Up @@ -55,3 +56,22 @@ StreamTransformer<ConnectivityResult, ConnectivityResult> startsWith(
},
);
}

StreamTransformer<ConnectivityResult, ConnectivityResult> checkIfHostIsAvailble(String host) {
return StreamTransformer<ConnectivityResult, ConnectivityResult>.fromHandlers(
handleData: (ConnectivityResult data, EventSink<ConnectivityResult> sink) async {
try {
final result = await InternetAddress.lookup(host);
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
sink.add(data);
return;
}

throw SocketException("");
} on SocketException catch (_) {
sink.add(ConnectivityResult.none);
}
},
handleDone: (EventSink<ConnectivityResult> sink) => sink.close(),
);
}
Loading