I have a Flutter application that communicates with the android native code through EventChannel and MethodChannel in order to start or stop an SDK and listen and display in the Flutter Ui the event generated by the SDK.
Everything work fine until I decide to stop and re-start the SDK through a floatingActionButton.
After pressing it the UI freezes and in the Console of Android studio I get the following Exception:
======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
A ticker was started twice.
A ticker that is already active cannot be started again without first stopping it.
The affected ticker was: _WidgetTicker(created by _GlowingOverscrollIndicatorState#d8a61)
The stack trace when the _WidgetTicker was actually created was:
#0 new Ticker.<anonymous closure> (package:flutter/src/scheduler/ticker.dart:67:40)
#1 new Ticker (package:flutter/src/scheduler/ticker.dart:69:6)
#2 new _WidgetTicker (package:flutter/src/widgets/ticker_provider.dart:271:81)
#3 TickerProviderStateMixin.createTicker (package:flutter/src/widgets/ticker_provider.dart:202:34)
#4 new _GlowController (package:flutter/src/widgets/overscroll_indicator.dart:333:33)
#5 _GlowingOverscrollIndicatorState.initState (package:flutter/src/widgets/overscroll_indicator.dart:187:27)
#6 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4728:57)
#7 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4561:5)
... Normal element mounting (27 frames)
#34 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3631:14)
#35 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6261:36)
#36 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6272:32)
... Normal element mounting (22 frames)
#58 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3631:14)
#59 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6261:36)
#60 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6272:32)
... Normal element mounting (261 frames)
#321 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3631:14)
#322 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6261:36)
#323 Element.updateChild (package:flutter/src/widgets/framework.dart:3383:18)
#324 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5684:32)
#325 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6284:17)
#326 Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#327 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4613:16)
#328 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4763:11)
#329 Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#330 StatefulElement.update (package:flutter/src/widgets/framework.dart:4795:5)
#331 Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#332 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4613:16)
#333 Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#334 ProxyElement.update (package:flutter/src/widgets/framework.dart:4943:5)
#335 Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#336 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4613:16)
#337 Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#338 ProxyElement.update (package:flutter/src/widgets/framework.dart:4943:5)
#339 _InheritedNotifierElement.update (package:flutter/src/widgets/inherited_notifier.dart:111:11)
#340 Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#341 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6130:14)
#342 Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#343 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4613:16)
#344 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4763:11)
#345 Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#346 StatefulElement.update (package:flutter/src/widgets/framework.dart:4795:5)
#347 Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#348 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6130:14)
#349 Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#350 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6130:14)
#351 Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#352 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4613:16)
#353 Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#354 ProxyElement.update (package:flutter/src/widgets/framework.dart:4943:5)
#355 Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#356 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4613:16)
#357 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4763:11)
#358 Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#359 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2578:33)
#360 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#361 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#362 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1145:15)
#363 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1082:9)
#364 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:996:5)
#368 _invoke (dart:ui/hooks.dart:150:10)
#369 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:270:5)
#370 _drawFrame (dart:ui/hooks.dart:114:31)
(elided 3 frames from dart:async)
When the exception was thrown, this was the stack:
#0 Ticker.start.<anonymous closure> (package:flutter/src/scheduler/ticker.dart:151:9)
#1 Ticker.start (package:flutter/src/scheduler/ticker.dart:158:6)
#2 _GlowController.pull (package:flutter/src/widgets/overscroll_indicator.dart:443:29)
#3 _GlowingOverscrollIndicatorState._handleScrollNotification (package:flutter/src/widgets/overscroll_indicator.dart:262:29)
#4 Element.visitAncestorElements (package:flutter/src/widgets/framework.dart:4091:39)
#5 Notification.dispatch (package:flutter/src/widgets/notification_listener.dart:83:13)
#6 DragScrollActivity.dispatchOverscrollNotification (package:flutter/src/widgets/scroll_activity.dart:472:135)
#7 ScrollPosition.didOverscrollBy (package:flutter/src/widgets/scroll_position.dart:918:15)
#8 ScrollPosition.setPixels (package:flutter/src/widgets/scroll_position.dart:282:9)
#9 ScrollPositionWithSingleContext.setPixels (package:flutter/src/widgets/scroll_position_with_single_context.dart:82:18)
#10 ScrollPositionWithSingleContext.applyUserOffset (package:flutter/src/widgets/scroll_position_with_single_context.dart:124:5)
#11 ScrollDragController.update (package:flutter/src/widgets/scroll_activity.dart:387:14)
#12 ScrollableState._handleDragUpdate (package:flutter/src/widgets/scrollable.dart:646:12)
#13 DragGestureRecognizer._checkUpdate.<anonymous closure> (package:flutter/src/gestures/monodrag.dart:449:55)
#14 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:198:24)
#15 DragGestureRecognizer._checkUpdate (package:flutter/src/gestures/monodrag.dart:449:7)
#16 DragGestureRecognizer.handleEvent (package:flutter/src/gestures/monodrag.dart:298:9)
#17 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:94:12)
#18 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:139:9)
#19 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:539:8)
#20 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:137:18)
#21 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:123:7)
#22 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:439:19)
#23 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:419:22)
#24 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:322:11)
#25 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:374:7)
#26 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:338:5)
#27 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:296:7)
#28 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:279:7)
#32 _invoke1 (dart:ui/hooks.dart:169:10)
#33 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:293:7)
#34 _dispatchPointerDataPacket (dart:ui/hooks.dart:88:31)
(elided 3 frames from dart:async)
Handler: "onUpdate"
Recognizer: VerticalDragGestureRecognizer#72195
start behavior: start
====================================================================================================
This is my java code in the Android folder (MainActivity.java):
public class MainActivity extends FlutterActivity implements ILogListener, IPositionEventListener {
private static final int PERMISSIONS_REQUEST_LOCATION = 2;
public static String authkey = "authkey";
public static String deviceID = "deviceID";
private MySDK mySDK;
private static final String channel = "channel_events_positions_updates";
private static final String channel1 = "mySDK";
private EventChannel.EventSink eventSink = null;
private Handler handler;
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
EventChannel event = new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), channel);
event.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object listener, EventChannel.EventSink eventSink) {
startListening(listener, eventSink);
}
@Override
public void onCancel(Object listener) {
cancelListening(listener);
}
});
new MethodChannel(flutterEngine.getDartExecutor(), channel1).setMethodCallHandler(
(call, result) -> {
if (call.method.equals("setAuthekyAndDeviceID")) {
if (call.argument("authkey") != null && call.argument("deviceID") != null) {
authkey = call.argument("authkey").toString();
deviceID = call.argument("deviceID").toString();
}
} else if (call.method.equals("stopService")) {
onStop();
} else {
result.notImplemented();
}
});
}
@Override
protected void onStop() {
if(mySDK!=null){
handler.removeCallbacksAndMessages(null);
eventSink = null;
mySDK.Stop();
super.onStop();
}
}
protected void startSDK() {
Set<String> missingPermissions = new HashSet<>();
ArrayList<String> perms = new ArrayList<>();
perms.add(Manifest.permission.ACCESS_FINE_LOCATION);
perms.add(Manifest.permission.ACCESS_COARSE_LOCATION);
perms.add(Manifest.permission.INTERNET);
perms.add(Manifest.permission.ACCESS_NETWORK_STATE);
perms.add(Manifest.permission.RECEIVE_BOOT_COMPLETED);
perms.add(Manifest.permission.WAKE_LOCK);
perms.add(Manifest.permission.VIBRATE);
perms.add(Manifest.permission.BLUETOOTH);
perms.add(Manifest.permission.BLUETOOTH_ADMIN);
perms.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
perms.add(Manifest.permission.READ_EXTERNAL_STORAGE);
checkPerms(missingPermissions, perms);
if (!missingPermissions.isEmpty()) {
// ask for permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(missingPermissions.toArray(new String[0]),
missingPermissions.size());
}
if (!missingPermissions.isEmpty()) {
return;
// will try again after onRequestPermissionsResult
}
}
buildSdk();
mySDK.Start();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSIONS_REQUEST_LOCATION) {
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
break;
}
}
this.getApplicationContext();
buildSdk();
mySDK.Start();
}
}
private void checkPerms(Set<String> missingPermissions, ArrayList<String> perms) {
for (String perm : perms) {
if (ContextCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) {
missingPermissions.add(perm);
}
}
}
private ConnectionConfig generateConnectionConfig() {
ConnectionConfig result = new ConnectionConfig();
result.setDeviceId(deviceID);
result.setAuthkey(authkey);
return result;
}
private void buildSdk() {
mySDK.Builder builder = new mySDK.Builder(this);
builder.setConnectionConfig(generateConnectionConfig());
builder.addLogListener(this);
builder.setPositionEventListener(this);
builder.setNotification(demo);
mySDK = builder.build();
mySDK.Start();
log("SDK Started");
}
public void log(String _message) {
log(_message, false, false);
}
@Override
public void log(String _message, boolean _vibrate, boolean _sound) {
sendMessageToEventChannel("[LOG]" +_message);
}
private void sendMessageToEventChannel(String _message) {
Runnable runnable = () -> {
if (eventSink != null) {
eventSink.success(_message);
}
};
handler.post(runnable);
}
@Override
public void onPositionEntry(Iterable<? extends BasicPosition> positions) {
if (positions != null) {
String position = positions.iterator().next().getName() + "\n" + positions.iterator().next().getAttributes();
sendMessageToEventChannel(position);
}
}
public void startListening(Object listener, EventChannel.EventSink event) {
handler = new Handler(Looper.getMainLooper());
eventSink = event;
startSDK();
}
public void cancelListening(Object listener) {
onStop();
}
}
And this is my dart class in which I manage the SDK:
class StateScreen extends StatefulWidget {
const StateScreen({Key? key, required this.authkey, required this.deviceID})
: super(key: key);
final String authkey;
final String deviceID;
State<StateScreen> createState() => _StateScreenState();
}
class _StateScreenState extends State<StateScreen> {
static const platform = MethodChannel('mySDK');
static const channel = const EventChannel('channel_events_positions_updates');
bool serviceStarted = false;
late StreamSubscription _positionEventSubscription;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My App'),
),
backgroundColor: Colors.white,
body: Center(
child: Column(
children: <Widget>[
PositionsView(logs: logs),
Padding(
padding: const EdgeInsets.only(top: 20.0),
child: ConstrainedBox(
constraints: new BoxConstraints(
maxHeight: 230.0,
),
child: new ListView.separated(
itemCount: names.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
separatorBuilder: (BuildContext context, int index) =>
const Divider(),
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.all(6),
child: Text(
'${names[index]}',
style: TextStyle(fontSize: 10),
),
);
},
),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
if (serviceStarted) {
serviceStarted = false;
stopSDK();
} else {
serviceStarted = true;
startSDK();
}
});
},
child: Icon(serviceStarted ? Icons.stop : Icons.play_arrow),
),
);
}
String logs = "";
List<String> names = <String>[];
void startSDK() async {
_setAuthkeyAndDeviceID(widget.authkey, widget.deviceID);
_positionEventSubscription = channel.receiveBroadcastStream().listen((msg) {
setState(() {
print(msg);
if (msg.toString().contains("[LOG]")) {
var log = msg.toString().replaceAll("[LOG]", "");
print(log);
names.add(log);
} else {
logs = msg;
}
});
});
}
void _disablePositionEventSubscription() {
_positionEventSubscription.cancel();
}
void stopSDK() async {
try {
await platform.invokeMethod('stopService');
_disablePositionEventSubscription();
super.dispose();
} on PlatformException catch (e) {
print("Failed to get: '${e.message}'.");
}
}
Future<void> _setAuthkeyAndDeviceID(String authkey, String deviceID) async {
try {
await platform.invokeMethod(
'setAuthekyAndDeviceID', {"authkey": authkey, "deviceID": deviceID});
} on PlatformException catch (e) {
print("Failed to get: '${e.message}'.");
}
}
}
Anyone know how can I fix this exception?