A highly customizable Flutter widget to render and interact with JSON objects.

Overview


Rows

The spreadsheet with superpowers !


JSON Data Explorer

A highly customizable widget to render and interact with JSON objects.

An animated image of the json widget interaction      An animated image of the search capabilities

Features

  • Expand and collapse classes and array nodes.
  • Dynamic search with highlight.
  • Configurable theme and interactions.
  • Configurable data display format.
  • Indentation guidelines.

Usage

The data to be displayed is managed by a store, the DataExplorerStore. In order to use all features from this package you need to register it in a Provider.

final DataExplorerStore store = DataExplorerStore();

/// ...
ChangeNotifierProvider.value(
  value: store,
  child:
/// ...

To load a json object, use DataExplorerStore.build nodes method.

store.buildNodes(json.decode(myJson));

To display the data explorer, you can use the JsonDataExplorer widget. The only required parameter is a list of node models, which you can take from the DataExplorerStore after a json was decoded.

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
    ),
    body: SafeArea(
      minimum: const EdgeInsets.all(16),
      child: ChangeNotifierProvider.value(
        value: store,
        child: Consumer<DataExplorerStore>(
          builder: (context, state, child) => JsonDataExplorer(
            nodes: state.displayNodes,
          ),
        ),
      ),
    ),
  );
}

This will display a decoded json using a default theme.

Check the /example app for more information on how to customize the look and feel of JsonDataExplorer widget.

Changing the look and feel

The JsonDataExplorer can be customized to fit different visual requirements.

Themes:

To change fonts and colors, use a DataExplorerTheme:

JsonDataExplorer(
  nodes: state.displayNodes,
  theme: DataExplorerTheme(
    rootKeyTextStyle: GoogleFonts.inconsolata(
      color: Colors.black,
      fontWeight: FontWeight.bold,
      fontSize: 16,
    ),
    propertyKeyTextStyle: GoogleFonts.inconsolata(
      color: Colors.black.withOpacity(0.7),
      fontWeight: FontWeight.bold,
      fontSize: 16,
    ),
    keySearchHighlightTextStyle: GoogleFonts.inconsolata(
      color: Colors.black,
      backgroundColor: const Color(0xFFFFEDAD),
      fontWeight: FontWeight.bold,
      fontSize: 16,
    ),
    focusedKeySearchHighlightTextStyle:
        GoogleFonts.inconsolata(
      color: Colors.black,
      backgroundColor: const Color(0xFFF29D0B),
      fontWeight: FontWeight.bold,
      fontSize: 16,
    ),
    valueTextStyle: GoogleFonts.inconsolata(
      color: const Color(0xFFCA442C),
      fontSize: 16,
    ),
    valueSearchHighlightTextStyle: GoogleFonts.inconsolata(
      color: const Color(0xFFCA442C),
      backgroundColor: const Color(0xFFFFEDAD),
      fontWeight: FontWeight.bold,
      fontSize: 16,
    ),
    focusedValueSearchHighlightTextStyle:
        GoogleFonts.inconsolata(
      color: Colors.black,
      backgroundColor: const Color(0xFFF29D0B),
      fontWeight: FontWeight.bold,
      fontSize: 16,
    ),
    indentationLineColor: const Color(0xFFE1E1E1),
    highlightColor: const Color(0xFFF1F1F1),
  ),
)

Formatter:

Changing the theme is not the only way to customize how the widget looks, Formatter methods can be used to change how key and values are converted into strings.

The default behavior to display json property names is key:, but this can be changed with a formatter:

JsonDataExplorer(
  nodes: state.displayNodes,
  propertyNameFormatter: (name) => '$name ->',
)

Now all property keys are displayed as key ->.

Changing property style based on value:

Property values style and onTap can be changed dynamically by using the valueStyleBuilder parameter. It expects a function that receives the property dynamic value and the current style, and returns a PropertyOverrides.

An example is adding interaction to values that contains links:

JsonDataExplorer(
  nodes: state.displayNodes,
  valueStyleBuilder: (value, style) {
    final isUrl = _valueIsUrl(value);
    return PropertyOverrides(
      style: isUrl
          ? style.copyWith(
              decoration: TextDecoration.underline,
            )
          : style,
      onTap: isUrl ? () => _launchUrl(value) : null,
    );
  },
)

Or, folowing the same principle, change how the value looks for specific value types:

JsonDataExplorer(
  nodes: state.displayNodes,
  valueStyleBuilder: (value, style) {
    if (value is num) {
      return PropertyOverrides(
        style: style.copyWith(
          color: Colors.blue,
        ),
      );
    } 
    return PropertyOverrides(
      style: style,
    );
  },
)

Custom widget components:

collapsableToggleBuilder allow the expand and collapse button that is displayed on root nodes to be changed. For example to use a simple implicitly animated widget:

JsonDataExplorer(
  nodes: state.displayNodes,
  collapsableToggleBuilder: (context, node) =>
      AnimatedRotation(
    turns: node.isCollapsed ? -0.25 : 0,
    duration: const Duration(milliseconds: 300),
    child: const Icon(Icons.arrow_drop_down),
  ),
)

rootInformationBuilder builds a widget that is displayed in classes and arrays root nodes. As an example, this can be used to display some information about its children nodes.

JsonDataExplorer(
  nodes: state.displayNodes,
  rootInformationBuilder: (context, node) => Text(
    node.isClass
        ? '{${(node.childrenCount)}}'
        : '[${node.childrenCount}]',
  ),
)

trailingBuilder builds a trailing widget in each node. The NodeViewModelState argument allows the widget to react to certain nodes properties. To build a widget that appears only when a node is currently focused for example:

JsonDataExplorer(
  nodes: state.displayNodes,
  trailingBuilder: (context, node) => node.isFocused
    ? Text("I'm focused :)")
    : const SizedBox(),
)

Search

DataExplorerStore provides search functionality using the search method. JsonDataExplorer widget already reacts to those state changes and highlights the search results. Refer to DataExplorerTheme to change the looks of search the results.

The focused result can be changed by calling the focusPreviousSearchResult and focusNextSearchResult methods.

Here is an example of a simple search bar, you can check a full example in the example folder.

Row(
  children: [
    Expanded(
      child: TextField(
        controller: searchController,
        onChanged: (term) => dataExplorerStore.search(term),
        maxLines: 1,
        decoration: const InputDecoration(
          hintText: 'Search',
        ),
      ),
    ),
    const SizedBox(
      width: 8,
    ),
    IconButton(
      onPressed: dataExplorerStore.focusPreviousSearchResult,
      icon: const Icon(Icons.arrow_drop_up),
    ),
    IconButton(
      onPressed: dataExplorerStore.focusNextSearchResult,
      icon: const Icon(Icons.arrow_drop_down),
    ),
  ],
),

Custom scroll widget

It is possible to implement your own scrolling by using the JsonAttribute widget to display each node.

A simple ListView.builder looks like this:

ListView.builder(
  itemCount: state.displayNodes.length,
  itemBuilder: (context, index) => JsonAttribute(
    node: state.displayNodes.elementAt(index),
    theme: DataExplorerTheme.defaultTheme,
  ),
),
Comments
  • fix: search matches length and highlight

    fix: search matches length and highlight

    The count of searchResults was not correct when there are multiple occurrences of a searchTerm in a node key or value. Apart from that, the highlight style was the same for every occurrence and should be a certain style for the occurrence that is focused and other style for the occurrences that are not.

    opened by nfsxreloader 0
  • feat: Dynamic property style and interaction

    feat: Dynamic property style and interaction

    Context

    Sometimes we want a property value to be displayed differently, or add onTap events to specific properties.

    At rows, this is needed to interact properly with URLs.

    Approach

    Added a valueStyleBuilder to the json widget, which is a function that can change the style and onTap events for a specific value.

    opened by victorbotamedi 0
  • feat: URL detection and action

    feat: URL detection and action

    Context

    We need to detect URLs inside JSON values and display them with the current style adding an underline and also opening the URL with the default platform action.

    Approach

    For simplicity, I made the property values to always detect if it is an URL. When it is an URL the _onTap action opens up the browser.

    In the future, we can implement a solution to make tap actions and dynamic property styles configurable. I have some ideas for that, but I think we can keep things simpler now.

    Let me know your thoughts.

    image

    opened by victorbotamedi 0
  • fix: Search focus on key and values

    fix: Search focus on key and values

    Depends on #3

    Context

    The current search focus didn't know if the focus was on a key or value match, so when a node was focused and had matches in both key and value, both matches were being highlighted at the same time.

    opened by victorbotamedi 0
  • feat: Add Theming options

    feat: Add Theming options

    Context

    We need to implement a solution to change the look and feel of the explorer widget.

    Approach

    Added a DataExplorerTheme and a set of builders to allow library users to customize how each node is displayed.

    At first, I didn't add DataExplorerTheme to any InheritedWidget, I'm passing along constructors, I'm not sure what's the best solution here. We can discuss.

    We are going to implement golden tests in a future PR.

    https://user-images.githubusercontent.com/5972363/159799633-1d1833c8-81c8-4ae1-80ab-27e6c30296e1.mp4

    opened by victorbotamedi 0
  • improv: add all expanded and all collapsed flags

    improv: add all expanded and all collapsed flags

    Adds areAllExpanded and areAllCollapsed flags on DataExplorerStore so that the dev can know when all the nodes are expanded or not and implement a behaviour based on that.

    opened by nfsxreloader 0
  • SPIKE: Initial data explorer implementation

    SPIKE: Initial data explorer implementation

    Related to

    https://github.com/rows/native-apps/issues/1011

    Context

    For the rows data explorer feature, we need to implement a widget to display and interact with JSON file contents.

    Approach

    Currently, the view implementation is using the ScrollablePositionedList, since the items don't have a fixed size, it helps to animate to a certain element in the list (search).

    When we load large JSONs, like the 25mb file sample, we have noticeable jank when fast scrolling with the scroll bar. This may be related to this issue, I noticed that on mobile platforms it works fine. If we do set a fixed item size, then this problem goes away, but since we break lines for long texts, we don't really have fixed item sizes. We could, however, implement those line breaks by ourselves and make their new items in the list, I didn't follow this route because of the currently supported JSON file size limits of rows, for those JSONs, this is not really an issue.

    Each json node (or "line" ) is a separate item in a list which then can be rendered by the builder, this is done for efficiency, but makes the management of items more challenging.

    For that I've built a structure that wraps the json elements into a know view model state, I call it NodeViewModelState. This view model contains the information of each individual node, and later on, a widget is built using its information. Those view models are built from a decoded JSON file by the buildViewModelNodes method, then we have a representation of the JSON object with all properties properly wrapped with our view model.

    That alone doesn't fix the problem, we still have to make a flat list from all json classes, so we can render them in a ListView. this is done by calling the flatten method. It takes an input of either Map<String,NodeViewModelState> or List<NodeViewModelState> and returns a List<NodeViewModelState> with all nodes that are visible. Children of collapsed nodes are not visible, so they are not returned by this, just the root node.

    At some point in time both buildViewModelNodes and flatten were done at the same time with a single pass, but the algorithm got super complex and hard to understand that I decided to split it into these two steps. By testing the ecxecution times of doing it all at once or doing it in two steps, the difference was a fraction of milliseconds to our worst-case (the GitHub 25MB JSON).

    Then why not just create a single widget for each json class?

    Because the performance sucks. It is ok for small json files, but even for medium files like the noble prize sample, the list got some jank because the items couldn't be recycled. The advantage is that the algorithm would be way easier to understand and collapsing nodes would be way easier.

    Meanwhile, I still need to add more tests and documentation. Once we get out of the SPIKE, we will make the theme configurable. This is not the final design :)

    Demo

    https://user-images.githubusercontent.com/5972363/158664038-92abcd28-a702-4292-8473-c0ba5a5191c4.mp4

    opened by victorbotamedi 0
Owner
rows
The spreadsheet with data, integrations and a slick sharing experience.
rows
Dart JS interop for Mermaid - The Javascript tool that makes use of a markdown based syntax to render customizable diagrams, charts and visualizations.

Mermaid (Dart) Dart JS interop for Mermaid - Javascript library that makes use of a markdown based syntax to render customizable diagrams, charts and

Tim Maffett 3 Dec 12, 2022
A package that provides a highly customizable sheet widget that snaps to different vertical & horizontal positions

Snapping Sheet A package that provides a highly customizable sheet widget that snaps to different vertical & horizontal positions Can adapt to scrolla

Adam Jonsson 364 Dec 6, 2022
The flutter_calendar_widget is highly customizable calendar widget.

flutter_calendar_widget The flutter_calendar_widget is highly customizable calendar widget. Not only can you change the style, but you can also change

dooboolab 4 Jun 24, 2022
Flutter package: Json Table Widget to create table from json array

Json Table Widget ?? Proudly Sponsored by FieldAssist Request a Demo This Flutter package provides a Json Table Widget for directly showing table from

Ayush P Gupta 193 Jan 7, 2023
This is a Flutter plugin that takes a JSON string and converts it onto a customizable Flutter Widget.

Colored JSON Convert JSON string into customizable widget. Getting Started ColoredJson is a stateless widget that produces a structured view of JSON s

null 4 May 20, 2022
Create highly customizable, simple, and controllable autocomplete fields for Flutter.

Field Suggestion Installing Depend on it Add this to your package's pubspec.yaml file: dependencies: field_suggestion: <latest_version> Install it Y

Ismael Shakverdiev 21 Oct 18, 2022
⚡️A highly customizable, powerful and easy-to-use alerting library for Flutter.

Flash ⚡️ A highly customizable, powerful and easy-to-use alerting library for Flutter. Website: https://sososdk.github.io/flash Specs This library all

null 368 Jan 5, 2023
Powerful, helpfull, extensible and highly customizable API's that wrap http client to make communication easier with Axelor server with boilerplate code free.

flutter_axelor_sdk Powerful, helpful, extensible and highly customizable API's that wrap http client to make communication easier with Axelor server w

Abd al-Rahman al-Ktefane 5 Dec 25, 2022
A highly customizable Flutter color picker.

FlexColorPicker FlexColorPicker is a customizable color picker for Flutter. The ColorPicker can show six different types of color pickers, three of wh

Rydmike 132 Dec 14, 2022
A highly customizable Flutter color picker.

FlexColorPicker FlexColorPicker is a customizable color picker for Flutter. The ColorPicker can show six different types of color pickers, three of wh

Rydmike 132 Dec 14, 2022
Add beautiful animated effects & builders in Flutter, via an easy, highly customizable unified API.

Flutter Animate A performant library that makes it simple to add almost any kind of animated effect in Flutter. Pre-built effects, like fade, scale, s

Grant Skinner 352 Dec 25, 2022
A Flutter widget for rendering static html as Flutter widgets (Will render over 80 different html tags!)

flutter_html A Flutter widget for rendering HTML and CSS as Flutter widgets. Screenshot 1 Screenshot 2 Screenshot 3 Table of Contents: Installing Curr

Matthew Whitaker 1.5k Jan 5, 2023
A JSON serialize class to convert 'to' and 'from' JSON format Enums, DateTime and any of your own classes.

A JSON serialize class to convert 'to' and 'from' JSON format Enums, DateTime and any of your own classes. Introduction Jsonize solves the problem of

null 2 Nov 17, 2022
Given a JSON string, this library will generate all the necessary Dart classes to parse and generate JSON.

JSON to Dart Given a JSON string, this library will generate all the necessary Dart classes to parse and generate JSON. This library is designed to ge

Javier Lecuona 1.2k Dec 25, 2022
Fluter-json - App Demonstrating JSON Data Parsing with various flutter widgets

users_list Flutter App to Demonstrate JSON Parsing Getting Started This project is a starting point for a Flutter application. A few resources to get

Khurram Rizvi 5 Jul 10, 2021
Json editor - A json editor on flutter

Features Support add comment; Support show errors for invalid json text; Pretty

Chan Young 12 Nov 18, 2022
State Persistence - Persist state across app launches. By default this library store state as a local JSON file called `data.json` in the applications data directory. Maintainer: @slightfoot

State Persistence Persist state across app launches. By default this library store state as a local JSON file called data.json in the applications dat

Flutter Community 70 Sep 28, 2022
Intel Corporation 238 Dec 24, 2022
A Flutter Package to render Mathematics, Physics and Chemistry Equations based on LaTeX

flutter_tex Contents About Demo Video Screenshots How to use? Android iOS Web Examples Quick Example TeXView Document TeXView Markdown TeXView Quiz Te

Shahzad Akram 220 Dec 21, 2022