Using an OEM debugger
If you are exclusively writing Flutter apps with Dart code and not using platform-specific libraries, or otherwise accessing platform-specific features, you can debug your code using your IDE’s debugger. Only the first section of this guide, Debugging Dart code, is relevant for you.
If you’re writing a platform-specific plugin or using platform-specific libraries written in Swift, ObjectiveC, Java, or Kotlin, you can debug that portion of your code using Xcode (for iOS) or Android Gradle (for Android). This guide shows you how you can connect two debuggers to your Dart app, one for Dart, and one for the OEM code.
Debugging Dart code
Use your IDE for standard Dart debugging. These instructions describe Android Studio, but you can use your preferred IDE with the Flutter and Dart plugins installed and configured.
Dart debugger
-
Open your project in Android Studio. If you don’t have a project yet, create one using the instructions in Test drive.
-
Simultaneously bring up the Debug pane and run the app in the Console view by clicking the bug icon (<img src=/flutter_website/assets/images/docs/testing/debugging/oem/debug-run.png alt=’Debug-run icon’>).
The first time you launch the app is the slowest. You should see the Debug pane appear at the bottom of the window that looks something like the following:
<img src=/flutter_website/assets/images/docs/testing/debugging/oem/debug-pane.png alt=’Debug pane’>{:width=”100%”}
You can configure where the debug pane appears, or even tear it off to its own window using the gear to the right in the Debug pane bar. This is true for any inspector in Android Studio.
-
Add a breakpoint on the
counter++
line. -
In the app, click the + button (FloatingActionButton, or FAB, for short) to increment the counter. The app pauses.
-
The following screenshot shows:
- Breakpoint in the edit pane.
- State of the app in the debug pane, when paused at the breakpoint.
-
this
variable expanded to display its values.
<img src=/flutter_website/assets/images/docs/testing/debugging/oem/debug-pane-action.png alt=’App status when hitting the set breakpoint’>{:width=”100%”}
You can step in, out, and over Dart statements, hot reload or resume the app, and use the debugger in the same way you’d use any debugger. The 5: Debug button toggles display of the debug pane.
Flutter inspector
There are two other features provided by the Flutter plugin that you might find useful. The Flutter inspector is a tool for visualizing and exploring the Flutter widget tree and helps you:
- Understand existing layouts
- Diagnose layout issues
Toggle display of the inspector using the vertical button to the right of the Android Studio window.
<img src=/flutter_website/assets/images/docs/testing/debugging/oem/flutter-inspector.png alt=’Flutter inspector’>
Flutter outline
The Flutter Outline displays the build method in visual form. Note that this might be different than the widget tree for the build method. Toggle display of the outline using the vertical button to the right of the AS window.
<img src=/flutter_website/assets/images/docs/testing/debugging/oem/flutter-outline.png alt=’screenshot showing the Flutter inspector’>{:width=”100%”}
The rest of this guide shows how to set up your environment to debug OEM code. As you’d expect, the process works differently for iOS and Android.
Debugging with Android Gradle (Android)
In order to debug OEM Android code, you need an app that contains OEM Android code. In this section, you’ll learn how to connect two debuggers to your app: 1) the Dart debugger and, 2) the Android Gradle debugger.
-
Create a basic Flutter app.
-
Replace
lib/main.dart
with the following example code from theurl_launcher
package:
// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'URL Launcher', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'URL Launcher'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { Future<void> _launched; Future<void> _launchInBrowser(String url) async { if (await canLaunch(url)) { await launch(url, forceSafariVC: false, forceWebView: false); } else { throw 'Could not launch $url'; } } Future<void> _launchInWebViewOrVC(String url) async { if (await canLaunch(url)) { await launch(url, forceSafariVC: true, forceWebView: true); } else { throw 'Could not launch $url'; } } Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) { if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else { return Text(''); } } @override Widget build(BuildContext context) { String toLaunch = 'https://flutter.dev'; return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Padding( padding: EdgeInsets.all(16.0), child: Text(toLaunch), ), ElevatedButton( onPressed: () => setState(() { _launched = _launchInBrowser(toLaunch); }), child: Text('Launch in browser'), ), Padding(padding: EdgeInsets.all(16.0)), ElevatedButton( onPressed: () => setState(() { _launched = _launchInWebViewOrVC(toLaunch); }), child: Text('Launch in app'), ), Padding(padding: EdgeInsets.all(16.0)), FutureBuilder<void>(future: _launched, builder: _launchStatus), ], ), ), ); } }
- Add the
url_launcher
dependency to the pubspec file, and run flutter pub get:
name: flutter_app
description: A new Flutter application.
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
url_launcher: ^3.0.3
cupertino_icons: ^0.1.2
dev_dependencies:
flutter_test:
sdk: flutter
-
Click the debug icon (<img src=/flutter_website/assets/images/docs/testing/debugging/oem/debug-run.png alt=’Debug-run icon’>) to simultaneously bring up the Debug pane and launch the app. Wait for the app to launch on the device, and for the debug pane to indicate Connected. (This can take a minute the first time but is faster for subsequent launches.) The app contains two buttons: 1) Launch in browser opens flutter.dev in your phone’s default browser and 2) Launch in app opens flutter.dev within your app.
<img src=/flutter_website/assets/images/docs/testing/debugging/oem/launch-flutter-dev.png alt=’screenshot containing two buttons for opening flutter.dev’>
-
Click the Attach debugger to Android process button ( <img src=/flutter_website/assets/images/docs/testing/debugging/oem/attach-process-button.png alt=’looks like a rectangle superimposed with a tiny green bug’> )
-
From the process dialog, you should see an entry for each connected device. Select show all processes to display available processes for each device.
-
Choose the process you want to attach to. In this case, it’s the
com.google.clickcount
(or com.company.app_name) process for the Motorola Moto G.<img src=/flutter_website/assets/images/docs/testing/debugging/oem/choose-process-dialog.png alt=’screenshot containing two buttons for opening flutter.dev’>{:width=”100%”}
-
In the debug pane, you should now see a tab for Android Debugger.
-
In the project pane, expand
app_name > android > app > src > main > java > io.flutter plugins . Double click GeneratedProjectRegistrant to open the Java code in the edit pane.
Both the Dart and OEM debuggers are interacting with the same process. User either, or both, to set breakpoints, examine stack, resume execution… In other words, debug!
<img src=/flutter_website/assets/images/docs/testing/debugging/oem/dart-debugger.png alt=’screenshot of Android Studio in the Dart debug pane.’>{:width=”100%”}
<img src=/flutter_website/assets/images/docs/testing/debugging/oem/android-debugger.png alt=’screenshot of Android Studio in the Android debug pane.’>{:width=”100%”}
Debugging with Xcode (iOS)
In order to debug OEM iOS code, you need an app that contains OEM iOS code. In this section, you’ll learn how to connect two debuggers to your app: 1) the Dart debugger and, 2) the Xcode debugger.
[PENDING]
Resources
The following resources have more information on debugging Flutter, iOS, and Android:
Flutter
- Debugging Flutter apps
- Flutter inspector, as well as the general DevTools docs.
- Performance profiling
Android
You can find the following debugging resources on developer.android.com.
iOS
You can find the following debugging resources on developer.apple.com.