fullstory_flutter 0.1.3 copy "fullstory_flutter: ^0.1.3" to clipboard
fullstory_flutter: ^0.1.3 copied to clipboard

Fullstory for Flutter mobile apps. Fullstory provides insightful analytics and near-magical session replay for web and mobile apps.

example/example.md

fullstory_flutter Examples #

From https://github.com/fullstorydev/fullstory-flutter/tree/main/example/lib

capture_status.dart #

import 'package:flutter/material.dart';
import 'package:fullstory_flutter/fs.dart';

// This uses FS.getCurrentSessionURL() to check if the session has already started
// and creates a FSStatusListener to be notified when a session starts

class CaptureStatus extends StatefulWidget {
  const CaptureStatus({
    super.key,
  });

  @override
  State<CaptureStatus> createState() => _CaptureStatusState();
}

// Use the FSStatusListener mixin on this class
class _CaptureStatusState extends State<CaptureStatus> with FSStatusListener {
  var status = "Loading...";
  var url = "";
  var id = "";
  var urlNow = "Press button to update";

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

    // grab the current session URL & ID in case it has already started
    FS.getCurrentSessionURL().then((url) => setState(() {
          if (url != null) {
            // if there is a url, we know the session started
            this.url = url;
            status = "Started";
          }
        }));
    FS.getCurrentSession().then((id) => setState(() {
          this.id = id ?? "";
        }));

    // set the status listener to handle future changes
    FS.setStatusListener(this);
  }

  @override
  void dispose() {
    super.dispose();
    // clear the current status listener (there can be only one!)
    FS.setStatusListener(null);
  }

  // This comes from FSStatusListener - the default implementation is a no-op
  @override
  void onFSSession(String url) {
    setState(() {
      status = "Started";
      this.url = url;
      FS.getCurrentSession().then((id) => setState(() {
            this.id = id ?? "";
          }));
    });
  }
  // Other events (session ended, disabled, error, etc.) are not currently supported, but may be added in a future version of the library

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Wrap(
          children: [
            TextButton(
                onPressed: () {
                  FS.shutdown();
                  // manually do this one since only the iOS SDK has a callback for it
                  setState(() {
                    status = "Shutdown";
                    url = "";
                    id = "";
                  });
                },
                child: const Text("Shutdown")),
            const TextButton(onPressed: FS.restart, child: Text("Restart")),
            TextButton(
                onPressed: () {
                  FS.getCurrentSessionURL(true).then((url) => setState(() {
                        urlNow = url ?? "";
                      }));
                },
                child: const Text("Update Timestamped URL"))
          ],
        ),
        SelectableText("Status: $status\nURL: $url\nNow: $urlNow\nID: $id"),
      ],
    );
  }
}

events.dart #

import 'package:flutter/material.dart';
import 'package:fullstory_flutter/fs.dart';

// Send custom events to Fullstory
// These will show up in the event list on the right side of session replays

class Events extends StatelessWidget {
  const Events({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Wrap(
      children: [
        TextButton(
          onPressed: () => FS.event("Name-only event"),
          child: const Text("Name-only event"),
        ),
        TextButton(
          onPressed: () => FS.event("Many properties event", {
            "string_val": "a string value",
            "int_val": 42,
            "double_val": 0.1,
            "bool_val": true,
            "null_val": null,
            "list_val": [1, 2, 3],

            // in playback, this is displayed as:
            // map_val.nested_map.val_bool: true
            // map_val.nested_string_str: nested string
            "map_val": {
              "nested_string": "nested string",
              "nested_map": {"val": true},
            },
            //"mixed_list_val": [4, "a", false], // not supported, error in playback
          }),
          child: const Text("Many properties event"),
        ),
        TextButton(
          onPressed: () => FS.event('Order Completed', {
            'orderId': '23f3er3d',

            // Not fully supported(as of Fullstory v1.54.0) - The products are silently dropped:"
            // "Note: Order Completed Events are not supported in Native Mobile as objects and arrays within arrays are not supported."
            // https://help.fullstory.com/hc/en-us/articles/360020623274-Sending-custom-event-data-into-Fullstory#Order%20Completed%20Events:~:text=Note%3A%20Order%20Completed%20Events%20are%20not%20supported%20in%20Native%20Mobile%20as%20objects%20and%20arrays%20within%20arrays%20are%20not%20supported.
            'products': [
              {'productId': '9v87h4f8', 'price': 20.00, 'quantity': 0.75},
              {'productId': '4738b43z', 'price': 12.87, 'quantity': 6},
            ],
          }),
          child: const Text('Order Completed event'),
        )
      ],
    );
  }
}

fs_version.dart #

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fullstory_flutter/fs.dart';

// Get the version of the underlying native Fullstory SDK (e.g. '1.54.0')

class FSVersion extends StatefulWidget {
  const FSVersion({super.key});
  @override
  State<FSVersion> createState() => _FSVersionState();
}

class _FSVersionState extends State<FSVersion> {
  String fsVersion = 'Unknown';
  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    String fsVersion;
    // Platform messages may fail, so we use a try/catch PlatformException.
    // We also handle the message potentially returning null.
    try {
      fsVersion = await FS.fsVersion() ?? 'Unknown Fullstory version';
    } on PlatformException {
      fsVersion = 'Failed to get Fullstory version.';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      this.fsVersion = fsVersion;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text('Fullstory version: $fsVersion\n');
  }
}

identity.dart #

import 'package:flutter/material.dart';
import 'package:fullstory_flutter/fs.dart';

class Identity extends StatefulWidget {
  const Identity({super.key});

  @override
  State<Identity> createState() => _IdentityState();
}

class _IdentityState extends State<Identity> {
  var level = FSLogLevel.info;
  var uid = '';
  var displayName = '';
  var email = '';

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      TextField(
        decoration: const InputDecoration(
          border: OutlineInputBorder(),
          hintText: 'displayName',
        ),
        onChanged: (value) => setState(() {
          displayName = value;
        }),
        // allow the keyboard to be hidden - why is this not the default behavior?
        onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),
      ),
      TextField(
        decoration: const InputDecoration(
          border: OutlineInputBorder(),
          hintText: 'email',
        ),
        onChanged: (value) => setState(() {
          email = value;
        }),
        onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),
      ),
      TextField(
        decoration: const InputDecoration(
          border: OutlineInputBorder(),
          hintText: 'uid',
        ),
        onChanged: (value) => setState(() {
          uid = value;
        }),
        onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),
      ),
      Wrap(
        children: [
          TextButton(
            child: const Text('Identify'),
            onPressed: () {
              FS.identify(uid);
            },
          ),
          TextButton(
            child: const Text('Identify w/ userVars'),
            onPressed: () {
              FS.identify(uid, {
                // email and displayName are used by Fullstory, everything else is arbitrary
                'source': 'identify',
                'when': DateTime.now().toString(),
                'displayName': displayName,
                'email': email,
                'extraInfo': 'foo'
              });
            },
          ),
          TextButton(
            child: const Text('setUserVars'),
            onPressed: () {
              FS.setUserVars({
                // ditto above: email and displayName are used by Fullstory, everything else is arbitrary
                'source': 'setUserVars',
                'when': DateTime.now().toString(),
                'displayName': displayName,
                'email': email,
                'membershipLevel': 'bar'
              });
            },
          ),
          TextButton(
            child: const Text('Anonymize'),
            onPressed: () {
              FS.anonymize();
            },
          ),
        ],
      ),
    ]);
  }
}

log.dart #

import 'package:flutter/material.dart';
import 'package:fullstory_flutter/fs.dart';

class Log extends StatefulWidget {
  const Log({super.key});

  @override
  State<Log> createState() => _LogState();
}

class _LogState extends State<Log> {
  var level = FSLogLevel.info;
  var message = "";

  // Write extra messages to the Fullstory log
  // What is captured depends on the logLevel setting in iOS & Android
  // All captured logs appear in

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      TextField(
        decoration: const InputDecoration(
          border: OutlineInputBorder(),
          hintText: 'Log message...',
        ),
        onChanged: (value) => message = value,
        // allow the keyboard to be hidden - why is this not the default behavior?
        onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),
      ),
      Row(
        children: [
          const Text("Level:"),
          DropdownMenu(
            dropdownMenuEntries: FSLogLevel.values
                .map<DropdownMenuEntry<FSLogLevel>>((FSLogLevel level) {
              return DropdownMenuEntry<FSLogLevel>(
                  value: level, label: level.name);
            }).toList(),
            initialSelection: level,
            onSelected: (value) => level = value!,
          ),
          TextButton(
              onPressed: () {
                FS.log(message: message, level: level);
              },
              child: const Text('Log'))
        ],
      ),
    ]);
  }
}

main.dart #

import 'package:flutter/material.dart';
import 'package:fullstory_flutter_example/webview.dart';

import 'capture_status.dart';
import 'identity.dart';
import 'log.dart';
import 'events.dart';
import 'fs_version.dart';

// Example app that demonstrates use of most Fullstory APIs

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int _selectedIndex = 0;

  static const List<Widget> _pages = <Widget>[
    CaptureStatus(),
    Identity(),
    Log(),
    Events(),
    FSVersion(),
    WebView(),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Fullstory Flutter test app'),
          leading: Builder(
            builder: (context) {
              return IconButton(
                icon: const Icon(Icons.menu),
                onPressed: () {
                  Scaffold.of(context).openDrawer();
                },
              );
            },
          ),
        ),
        body: _pages[_selectedIndex],
        drawer: Builder(builder: (context) {
          return Drawer(
            // Add a ListView to the drawer. This ensures the user can scroll
            // through the options in the drawer if there isn't enough vertical
            // space to fit everything.
            child: ListView(
              // Important: Remove any padding from the ListView.
              //padding: EdgeInsets.zero,
              children: [
                // generate a list of menu entries from the list of pages
                for (var i = 0; i < _pages.length; i++)
                  ListTile(
                    title: Text(_pages[i].toString()),
                    selected: _selectedIndex == i,
                    onTap: () {
                      // Update the state of the app
                      _onItemTapped(i);
                      // Then close the drawer
                      Navigator.pop(context);
                    },
                  ),
              ],
            ),
          );
        }),
      ),
    );
  }
}

webview.dart #

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

// Basic webview example.
// No Fullstory APIs are used here, and the webview's contents are not currently visible in playback.

class WebView extends StatefulWidget {
  const WebView({super.key});

  @override
  State<WebView> createState() => _WebViewState();
}

class _WebViewState extends State<WebView> {
  late final WebViewController controller;

  @override
  void initState() {
    super.initState();
    controller = WebViewController()
      ..loadRequest(
        Uri.parse('https://www.fullstory.com/'),
      );
  }

  @override
  Widget build(BuildContext context) {
    return WebViewWidget(controller: controller);
  }
}
3
likes
160
pub points
29%
popularity

Publisher

verified publisherfullstory.com

Fullstory for Flutter mobile apps. Fullstory provides insightful analytics and near-magical session replay for web and mobile apps.

Homepage

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on fullstory_flutter