Flutter Exception caught by gesture: a ticker was started twice

497 views Asked by At

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?

0

There are 0 answers