dynatrace_flutter_plugin 3.331.1
dynatrace_flutter_plugin: ^3.331.1 copied to clipboard
The Dynatrace Flutter plugin helps auto-instrument your Flutter app with Dynatrace OneAgent for Android and iOS. It also provides an API to add manual instrumentation.
example/lib/main.dart
import 'dart:io';
import 'package:dynatrace_flutter_plugin/dynatrace_flutter_plugin.dart';
import 'package:dynatrace_flutter_plugin_example/crash_reporting.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
const String HOME_NAV = 'homeNav';
const String TEST_NAV = 'testNav';
const String CRASH_REPORTING_NAV = 'crashReportingNav';
MethodChannel platform = const MethodChannel('com.dynatrace.flutter/crash');
void main() {
Dynatrace().start(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Dynatrace Test App',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
onUnknownRoute: (settings) => MaterialPageRoute(
builder: (context) => UndefinedView(
name: settings.name,
)),
initialRoute: HOME_NAV,
routes: {
HOME_NAV: (context) => const MyHomePage(),
TEST_NAV: (context) => const TestNav(),
CRASH_REPORTING_NAV: (context) => const CrashTestScreen(),
},
navigatorObservers: [DynatraceNavigationObserver()],
home: const MyHomePage(),
);
}
}
class TestNav extends StatelessWidget {
const TestNav({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FloatingActionButton(
child: const Icon(Icons.navigate_before),
onPressed: () {
Navigator.pushNamed(context, HOME_NAV);
},
),
),
);
}
}
class UndefinedView extends StatelessWidget {
const UndefinedView({Key? key, this.name}) : super(key: key);
final String? name;
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('No route defined here!'),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Text appBarText = const Text('Dynatrace Test App');
static late BuildContext _context;
static Map<String, VoidCallback> actionsMap = {
'Single Action': _singleAction,
'Sub Action': _subAction,
'Web Action': _webAction,
'Web Action Override': _webActionOverrideHeader,
'Web Action Full Manual': _webActionFullManualInstr,
'HTTP: sendHttpRequestEvent': _sendHttpRequestEvent,
'HTTP: Add Event Modifier': _addHttpEventModifier,
'HTTP: Remove Event Modifier': _removeHttpEventModifier,
'Report values': _reportAll,
'Make Navigation': _makeNavigation,
'Open Crash Reporting': _openCrashReporting,
'Flush data': _flushData,
'Tag user': _tagUser,
'End Session': _endSession,
'setGpsLocation: Hawaii': _setGpsLocationHawaii,
'User Privacy Options : All Off': _userPrivacyOptionsAllOff,
'User Privacy Options : All On': _userPrivacyOptionsAllOn,
'getUserPrivacyOptions': () async {
final options = await Dynatrace().getUserPrivacyOptions();
print('User Privacy Options Crash:');
print(options.crashReportingOptedIn);
print('User Privacy Options Level:');
print(options.dataCollectionLevel);
},
'Send Event': _sendEvent,
'Send Session Property Event': _sendSessionPropertyEvent,
'Add Event Modifier': _addEventModifier,
'Remove Event Modifier': _removeEventModifier,
};
@override
Widget build(BuildContext context) {
_context = context;
final sController = ScrollController();
return Scaffold(
appBar: AppBar(
title: appBarText,
automaticallyImplyLeading: false,
),
body: Scrollbar(
controller: sController,
child: SingleChildScrollView(
controller: sController,
padding: const EdgeInsets.all(20),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
for (int i = 0; i < actionsMap.keys.length; i++)
Container(
width: 280,
height: 45,
padding: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: actionsMap.values.elementAt(i),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
),
child: Text(
actionsMap.keys.elementAt(i),
style: const TextStyle(color: Colors.white),
),
),
),
],
),
),
),
),
);
}
static void _makeNavigation() {
Navigator.pushNamed(_context, TEST_NAV);
}
static void _singleAction() {
print('Single action called flutter');
final myAction = Dynatrace().enterAction('MyButton tapped - Single Action');
//Perform the action and whatever else is needed.
myAction.leaveAction();
}
static void _subAction() {
final myAction = Dynatrace().enterAction('MyButton tapped - Sub Action');
final mySubAction = myAction.enterAction('MyButton Sub Action');
//Perform the action and whatever else is needed.
mySubAction.leaveAction();
myAction.leaveAction();
}
static Future<void> _webAction() async {
final client = Dynatrace().createHttpClient();
const url = 'https://dynatrace.com';
final webAction = Dynatrace().enterAction('Web Action - $url');
try {
await client.get(Uri.parse(url));
} catch (error) {
// insert error handling here
} finally {
client.close();
webAction.leaveAction();
}
}
static Future<void> _webActionOverrideHeader() async {
final client = Dynatrace().createHttpClient();
final action = Dynatrace().enterAction('MyButton tapped - Web Action Override');
final url = 'https://dynatrace.com';
try {
final response = await client.get(
Uri.parse(url),
headers: {
action.getRequestTagHeader(): await action.getRequestTag(url),
},
);
print(response);
} catch (error) {
// insert error handling here
} finally {
client.close();
action.leaveAction();
}
}
static Future<void> _webActionFullManualInstr() async {
final action = Dynatrace().enterAction('MyButton tapped - Web Action Full Manual');
final url = 'https://dynatrace.com';
final timing = await action.createWebRequestTiming(url);
if (timing.getRequestTag().isNotEmpty) {
// Use the timing to track the request
timing.startWebRequestTiming();
try {
final response = await http.get(
Uri.parse(url),
headers: {
timing.getRequestTagHeader(): timing.getRequestTag(),
},
);
timing.stopWebRequestTiming(response.statusCode, response.reasonPhrase);
print(response);
} catch (error) {
// insert error handling here
timing.stopWebRequestTiming(-1, error.toString());
} finally {
action.leaveAction();
}
} else {
print('The timing tag is null or empty, the request will not be tracked.');
}
}
static void _reportAll() {
final myAction = Dynatrace().enterAction('MyButton tapped - Report values');
myAction.reportStringValue('ValueNameString', 'ImportantValue');
myAction.reportIntValue('ValueNameInt', 1234);
myAction.reportDoubleValue('ValueNameDouble', 123.4567);
myAction.reportEvent('ValueNameEvent');
myAction.reportError('ValueNameError', 408);
myAction.leaveAction();
}
static void _openCrashReporting() {
Navigator.pushNamed(_context, CRASH_REPORTING_NAV);
}
static void _flushData() {
Dynatrace().flushEvents();
}
static void _tagUser() {
Dynatrace().identifyUser('User XY');
}
static void _endSession() {
Dynatrace().endSession();
}
static void _setGpsLocationHawaii() {
// set GPS coords to Hawaii
Dynatrace().setGPSLocation(19, 155);
}
static void _userPrivacyOptionsAllOff() {
Dynatrace().applyUserPrivacyOptions(UserPrivacyOptions(DataCollectionLevel.Off, false));
}
static void _userPrivacyOptionsAllOn() {
Dynatrace().applyUserPrivacyOptions(UserPrivacyOptions(DataCollectionLevel.UserBehavior, true));
}
static void _sendEvent() {
Dynatrace().sendEvent(EventData(eventProperties: {
'event_properties.valid': 'value',
}));
}
static void _sendSessionPropertyEvent() {
Dynatrace().sendSessionPropertyEvent(SessionPropertyEventData(sessionProperties: {
'session_properties.custom_key': 'custom_value',
'session_properties.user_segment': 'premium',
}));
}
// Simple example modifier that adds a tag to events
static EventModifier? _exampleModifier;
static void _addEventModifier() {
_exampleModifier ??= (event) {
final modified = Map<String, dynamic>.from(event);
modified['event_properties.example.tag'] = 'example-tag';
return modified;
};
Dynatrace().addEventModifier(_exampleModifier!);
}
static void _removeEventModifier() {
if (_exampleModifier != null) {
Dynatrace().removeEventModifier(_exampleModifier!);
}
}
static Future<void> _sendHttpRequestEvent() async {
final client = HttpClient();
final url = 'https://dynatrace.com';
// Time the request
final stopwatch = Stopwatch()..start();
try {
final response = await http.get(Uri.parse(url));
stopwatch.stop();
print(response);
// Report the request
Dynatrace().sendHttpRequestEvent(HttpRequestEventData(
url: url,
method: 'GET',
statusCode: response.statusCode,
duration: stopwatch.elapsedMilliseconds,
bytesReceived: response.bodyBytes.length,
));
} catch (error) {
// insert error handling here
stopwatch.stop();
// Report the request
Dynatrace().sendHttpRequestEvent(HttpRequestEventData(
url: url,
method: 'GET',
duration: stopwatch.elapsedMilliseconds,
throwable: error,
));
}
}
static HttpEventModifier? _httpModifier;
static void _addHttpEventModifier() {
_httpModifier ??= (event, request, response, error, stack) {
// Example enrichment under allowed namespace
event['event_properties.http.example'] = '${request.method} ${request.url.host}';
// Optional redaction example of url.full
final full = event['url.full']?.toString();
if (full != null) {
event['url.full'] = full.replaceAll(RegExp(r'/users/[^/]+'), '/users/{id}');
}
return event;
};
Dynatrace().addHttpEventModifier(_httpModifier!);
}
static void _removeHttpEventModifier() {
if (_httpModifier != null) {
Dynatrace().removeHttpEventModifier(_httpModifier!);
}
}
}