Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new listener builder widget #56

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
7 changes: 7 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import './demo_page.dart';
import './widgets/demo_1.dart';
import './widgets/demo_2.dart';
import './widgets/demo_3.dart';
import './widgets/demo_4.dart';

void main() => runApp(const MyApp());

Expand Down Expand Up @@ -38,6 +39,12 @@ class MyApp extends StatelessWidget {
},
child: const Text('Demo 3'),
),
ElevatedButton(
onPressed: () {
navigate(context, const Demo4());
},
child: const Text('Listener Demo'),
),
],
);
},
Expand Down
33 changes: 33 additions & 0 deletions example/lib/widgets/demo_4.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:flutter_offline/flutter_offline.dart';

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

@override
Widget build(BuildContext context) {
return OfflineListener(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add this to the README?

debounceDuration: Duration.zero,
listenWhen: (c) => c == ConnectivityResult.mobile,
listenerBuilder: (context, connectivity) {
// for ex showing a snackbar when using mobile internet if app is downloading something big.
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'Alert: Switched to mobile internet, consider switching to wifi instead.')));
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text(
'There are no bottons to push :)',
),
Text(
'Try switching your internet to wifi and mobile data',
),
],
),
),
);
}
}
4 changes: 3 additions & 1 deletion lib/flutter_offline.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
library flutter_offline;

export 'package:connectivity_plus/connectivity_plus.dart' show ConnectivityResult;
export 'package:connectivity_plus/connectivity_plus.dart'
show ConnectivityResult;

export 'src/main.dart';
export 'src/offline_listener.dart';
110 changes: 110 additions & 0 deletions lib/src/offline_listener.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import 'dart:async';

import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_offline/src/utils.dart';
import 'package:network_info_plus/network_info_plus.dart';

import 'main.dart';

typedef ListenerBuilder<T> = void Function(BuildContext context, T value);
typedef ListenWhen<T> = bool Function(ConnectivityResult);

class OfflineListener extends StatefulWidget {
factory OfflineListener({
Key? key,
required Widget child,
ListenWhen? listenWhen,
Duration debounceDuration = kOfflineDebounceDuration,
ListenerBuilder<ConnectivityResult>? listenerBuilder,
WidgetBuilder? errorBuilder,
}) {
return OfflineListener.initialize(
key: key,
connectivityService: Connectivity(),
wifiInfo: NetworkInfo(),
debounceDuration: debounceDuration,
errorBuilder: errorBuilder,
listenWhen: listenWhen,
listenerBuilder: listenerBuilder,
child: child,
);
}

@visibleForTesting
const OfflineListener.initialize({
Key? key,
required this.connectivityService,
required this.wifiInfo,
required this.child,
this.listenWhen,
this.listenerBuilder,
this.debounceDuration = kOfflineDebounceDuration,
this.errorBuilder,
}) : super(key: key);

/// Override connectivity service used for testing
final Connectivity connectivityService;

/// Specify when to listen
final ListenWhen? listenWhen;

/// Listen to new updates on connectivity. Only notifies when listenWhen returns true.
///
/// If listenWhen is null, will listen to all updates.
final ListenerBuilder<ConnectivityResult>? listenerBuilder;

final NetworkInfo wifiInfo;

/// Debounce duration from epileptic network situations
final Duration debounceDuration;

/// The widget below this widget in the tree.
final Widget child;

/// Used for building the error widget incase of any platform errors
final WidgetBuilder? errorBuilder;

@override
OfflineListenerState createState() => OfflineListenerState();
}

class OfflineListenerState extends State<OfflineListener> {
late Stream<ConnectivityResult> _connectivityStream;

@override
void initState() {
super.initState();

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

_listenToChanges();
}

void _listenToChanges() {
_connectivityStream.listen((data) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you dispose of this subscription?

if (widget.listenWhen?.call(data) ?? true) {
widget.listenerBuilder?.call(context, data);
}
});
}

@override
Widget build(BuildContext context) {
return widget.child;
}
}

class OfflineListenerError extends Error {
OfflineListenerError(this.error);

final Object error;

@override
String toString() => error.toString();
}