DEV Community

Cover image for AI-Powered Smart Flutter Spline Chart for Web Traffic Analytics
Jollen Moyani for Syncfusion, Inc.

Posted on • Originally published at syncfusion.com on

AI-Powered Smart Flutter Spline Chart for Web Traffic Analytics

TL;DR: Let’s see how to visualize web traffic analytics using the Syncfusion Flutter Spline Chart with AI-powered data prediction. We’ll collect web traffic data, display it in a Spline Chart, and use the google_generative_ai package to predict missing values. The chart will differentiate between actual and predicted data with custom colors. We’ll also enhance visualization with axis customization, tooltips, and an AI button for real-time preprocessing.

Welcome to the Chart of the Week blog series!

In today’s technology-driven landscape, integrating artificial intelligence (AI) with intuitive data visualization tools is transforming how we understand complex datasets. In this blog, we’ll explore how to visualize web traffic analytics data using the Syncfusion Flutter Spline Chart combined with AI.

We’ll visualize web traffic data with Flutter Spline Chart and predict the missing data using the google_generative_ai Flutter package.

Visualizing the web traffic analytics data using Flutter Spline Chart

Step 1: Gather the data

Start by collecting web traffic data counts from the Web Traffic Dataset as of January 25, 2020.

Step 2: Define and load data into the chart

Create a webTraffic class that includes properties for storing information about a web traffic timestamp, traffic count, and its color appearance to differentiate the AI-predicted and pre-defined data.

class _WebTraffic {
  _WebTraffic(this.timeStamp, this.trafficCount, [this.color]);

  final DateTime timeStamp;
  final double? trafficCount;
  Color? color;
}
Enter fullscreen mode Exit fullscreen mode

The following is the example webTrafficData list gathered from the report and used in the chart.

Note: For data pre-processing purposes, some values in the web traffic dataset have been intentionally set to null.

late List<_WebTraffic> _webTrafficData;

@override
void initState() {
  _webTrafficData = _generateWebTrafficData();
  super.initState();
}

List<_WebTraffic> _generateWebTrafficData() {
  return <_WebTraffic>[
    _WebTraffic(DateTime(2020, 1, 25, 00, 30), 273),
    _WebTraffic(DateTime(2020, 1, 25, 01, 30), 75),
    _WebTraffic(DateTime(2020, 1, 25, 02, 30), 193),
    _WebTraffic(DateTime(2020, 1, 25, 03, 30), null),
    _WebTraffic(DateTime(2020, 1, 25, 04, 30), 53),
    _WebTraffic(DateTime(2020, 1, 25, 05, 30), 156),
    _WebTraffic(DateTime(2020, 1, 25, 06, 30), 98),
    _WebTraffic(DateTime(2020, 1, 25, 07, 30), 291),
    _WebTraffic(DateTime(2020, 1, 25, 08, 30), null),
    _WebTraffic(DateTime(2020, 1, 25, 09, 30), 1020),
    _WebTraffic(DateTime(2020, 1, 25, 10, 30), 1155),
    _WebTraffic(DateTime(2020, 1, 25, 11, 30), null),
    _WebTraffic(DateTime(2020, 1, 25, 12, 30), 1484),
    _WebTraffic(DateTime(2020, 1, 25, 13, 30), 770),
    _WebTraffic(DateTime(2020, 1, 25, 14, 30), null),
    _WebTraffic(DateTime(2020, 1, 25, 15, 30), 990),
    _WebTraffic(DateTime(2020, 1, 25, 16, 30), 730),
    _WebTraffic(DateTime(2020, 1, 25, 17, 30), 1105),
    _WebTraffic(DateTime(2020, 1, 25, 18, 30), null),
    _WebTraffic(DateTime(2020, 1, 25, 19, 30), 645),
    _WebTraffic(DateTime(2020, 1, 25, 20, 30), 1038),
    _WebTraffic(DateTime(2020, 1, 25, 21, 30), 1163),
    _WebTraffic(DateTime(2020, 1, 25, 22, 30), 78),
    _WebTraffic(DateTime(2020, 1, 25, 23, 30), null),
  ];
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Building the Flutter Spline Chart

Now, configure the Flutter Charts control by following the guidelines provided in this documentation. In a chart, the axes define the scales along which data is plotted. Customizing these axes enhances both readability and visual appeal.

Next, create a SplineSeries object and bind the _webTrafficData list as the data source. Map the web traffic time to the x-axis and the traffic count to the y-axis.

To visually distinguish between pre-defined data and missing data points, we’ll use the pointColorMapper property in the series. Assign blue to pre-defined data points and red to missing ones.

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: SfCartesianChart(
      primaryXAxis: DateTimeAxis(),
      primaryYAxis: NumericAxis(),
      series: _buildSpLineSeries(),
    ),
  );
}

/// Returns the list of Cartesian Spline series.
List<SplineSeries<_WebTraffic, DateTime>> _buildSpLineSeries() {
  return <SplineSeries<_WebTraffic, DateTime>>[
    SplineSeries<_WebTraffic, DateTime>(
      dataSource: _webTrafficData,
      xValueMapper: (_WebTraffic data, int index) => data.timeStamp,
      yValueMapper: (_WebTraffic data, int index) => data.trafficCount,
      pointColorMapper: (_WebTraffic data, int index) => data.color,
      animationDuration: 0,
      legendItemText: 'Web traffic',
      markerSettings: MarkerSettings(isVisible: true),
    ),
  ];
 }
 );
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Customize the Flutter Spline Chart appearance

For the primaryXAxis, use a DatetimeAxis to represent web traffic timestamps. Customize its appearance by setting the dashArray property in the majorGridLines and adjusting the dateFormat to display time in a 24-hour format on the x-axis. Utilize the ChartAxisLabel class to modify the text and style of axis labels as needed. Additionally, use the axisLabelFormatter callback to further customize the format of the axis labels.

For the primaryYAxis, use a NumericAxis to represent web traffic counts. Set the title of the y-axis and customize the fontStyle and weight of the axis labels. Adjust the color and width of the majorTickLines and axisLine, and remove the majorGridLines for a cleaner appearance.

To customize the marker appearance, we’ll set the fill color to match the series color and the border color to white by overriding the onMarkerRender callback.

Let’s enhance the trackball’s appearance by customizing the trackball line color and tooltip text using the onTrackballPositionChanging callback. Use the builder property to create a custom tooltip that displays the web traffic timestamp and count. In the onPaint method, improve the trackball marker UI by drawing a custom marker for better visualization.

/// Return the Cartesian Chart with Spline series.
SfCartesianChart _buildCartesianChart(bool isMobile) {
  return SfCartesianChart(
    plotAreaBorderWidth: 0.1,
    legend: const Legend(
      isVisible: true,
      position: LegendPosition.top,
    ),
    title: ChartTitle(
      text: 'AI-Powered Website Traffic Analytics with Data Preprocessing',
    ),
    primaryXAxis: DateTimeAxis(
      minimum: DateTime(2020, 1, 25, 00, 00),
      maximum: DateTime(2020, 1, 25, 24, 00),
      interval: isMobile ? 4 : 1,
      dateFormat: DateFormat().add_H(),
      majorGridLines: const MajorGridLines(dashArray: [4, 4, 4]),
      axisLabelFormatter: (AxisLabelRenderDetails args) {
        if (args.value == 1579977000000) {
          return ChartAxisLabel('24', args.textStyle);
        }
        return ChartAxisLabel(args.text, args.textStyle);
      },
    ),
    primaryYAxis: const NumericAxis(
      minimum: 0,
      maximum: 1600,
      interval: 200,
      majorGridLines: MajorGridLines(width: 0),
    ),
    onMarkerRender: (MarkerRenderArgs args) {
      // Set series color as marker color.
      args.color = args.borderColor;
      // Improve the UI by setting white color for the marker border.
      args.borderColor = Colors.white;
    },
    onTrackballPositionChanging: (TrackballArgs args) {
      if (args.chartPointInfo.chartPoint != null) {
        args.chartPointInfo.chartPoint!.color = args.chartPointInfo.color;
      }
    },
    trackballBehavior: TrackballBehavior(
    ......
    ),
    series: _buildSpLineSeries(),
  );
}
Enter fullscreen mode Exit fullscreen mode

Refer to the following image.

Visualizing the web traffic analytics data using Flutter Spline Chart

Visualizing the web traffic analytics data using Flutter Spline Chart

Add an AI button for data pre-processing

Let’s add a custom button (AI Button) above the chart. When the button is tapped, a circular progress indicator will appear to show that data is loading, and the AI prediction logic will be processed. While we will cover the AI prediction logic in the upcoming sections, we can begin by designing the button with an appealing user interface.

Refer to the following code example.

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Stack(
      children: [
        _buildCartesianChart(),
        _buildCircularProgressIndicator(),
        _buildAIButton(),
      ],
    ),
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, we have displayed a button and a loading indicator within a stack to show both elements above the chart.

Customizing the AI button

Let’s enhance the UI of the AIButton by customizing it with an icon and text. Then, position the button at the top-right corner above the Flutter Spline Chart. Additionally, we’ll add tooltip support for the AIButton to indicate its purpose.

Refer to the following code example.

/// Builds the customized AI prediction button.
  Widget _buildAIButton() {
    return Align(
      alignment: Alignment.topRight,
      child: Padding(
        padding: EdgeInsets.fromLTRB(0, 30, 20, 0),
        child: Tooltip(
          message: 'Data preprocessing',
          decoration: BoxDecoration(
            color: Colors.black,
            borderRadius: BorderRadius.circular(8.0),
          ),
          child: _AIButton(
            onPressed: () {
              _loadNewData();
            },
          ),
        ),
      ),
    );
  }
Enter fullscreen mode Exit fullscreen mode
class _AIButton extends StatefulWidget {
  const _AIButton({
    required this.onPressed,
  });

  final VoidCallback onPressed;

  @override
  State<_AIButton> createState() => _AIButtonState();
}

class _AIButtonState extends State<_AIButton>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
  bool _isPressed = false; // Track the pressed state

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    )..repeat(reverse: true);

    _animation = Tween<double>(begin: 0.9, end: 1.1).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (BuildContext context, Widget? child) {
        return Transform.scale(
          scale: _isPressed ? 1 : _animation.value,
          child: ElevatedButton(
            style: ElevatedButton.styleFrom(
              iconColor: Theme.of(context).primaryColor,
              padding: const EdgeInsets.all(10),
            ),
            onPressed: () {
              setState(() {
                _isPressed = !_isPressed; // Toggle the pressed state
              });
              widget.onPressed();
            },
            child: Row(
              mainAxisSize: MainAxisSize.min,
              spacing: 5,
              children: [
                Image.asset(
                  'assets/ai_assist_view.png',
                  height: 30,
                  width: 30,
                  color: Colors.white,
                ),
                const Text(
                  'Generate AI Data',
                  style: TextStyle(color: Colors.white, fontSize: 12),
                ),
              ],
            ),
          ),
        );
      },
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super. Dispose();
  }
}
Enter fullscreen mode Exit fullscreen mode

Refer to the following image.

Flutter Spline Chart with Customized AI button

Flutter Spline Chart with Customized AI button

Adding circular progress indicator

When the AI button is tapped, the CircularProgressIndicator appears above the chart. We use the ValueListenableBuilder class to update the state of the loading indicator based on the *_**isLoadingNotifier* property.

/// Builds the circular progress indicator while updating the predicted data.
  Widget _buildCircularProgressIndicator() {
    return ValueListenableBuilder(
      valueListenable: _isLoadingNotifier,
      builder: (BuildContext context, bool value, Widget? child) {
        return Visibility(
          visible: _isLoadingNotifier.value,
          child: const Center(
            child: CircularProgressIndicator(),
          ),
        );
      },
    );
  }
Enter fullscreen mode Exit fullscreen mode

Refer to the following image.

Flutter Spline Chart with a circular progress indicator

Flutter Spline Chart with a circular progress indicator

Implementing AI prediction logic in the Flutter Spline Chart

Now, we’ll implement the following AI prediction logic in the onPressed event of the AIButton. Afterward, we will update the Flutter Spline Chart with the missing predicted data points.

Step 1: Create a prompt to request the data pre-processing

First, create a structured request for cleaning and completing traffic data. This prompt begins by setting defined rules to ensure that all data values adhere to the precise yyyy-MM-dd-HH-m-ss:Value format.

Each data entry will be formatted using dateFormat, and mapped from the existing _webTrafficData, converting timestamps and traffic counts into a unified string. The resulting data string will be assembled according to the prescribed rules, ensuring that the missing values are handled properly while preventing the inclusion of extraneous information.

String _generatePrompt() {
    final String rules =
        'Only include values in the yyyy-MM-dd-HH-m-ss:Value format,'
        'and ensure they strictly adhere to this format without any additional explanations.'
        'Unwanted content should be strictly avoided.';

    final String data = _webTrafficData
        .map((d) =>
            "${DateFormat('yyyy-MM-dd-HH-m-ss').format(d.timeStamp)}: ${d.trafficCount}")
        .join('\n');

    final String prompt =
        'Clean the following traffic data and fill in any missing values:\n$data\n'
        'The cleaned output should strictly follow the yyyy-MM-dd-HH-m-ss:Value format.\n$rules';

    return prompt;
  }
Enter fullscreen mode Exit fullscreen mode

Step 2: Obtain a response from Google AI for the request

To obtain a response from the AI based on our request, we can import the google_generative_ai Flutter package. Make sure that you have added this dependency to the pubspec.yaml file beforehand.

The google_generative_ai package is used to interact with Google AI and retrieve responses to our requests. However, to generate and receive responses from the AI, you will need to create an API key via Google AI for Developers.

Disclaimer: This sample uses the google_generative_ai package purely for demonstration purposes. Before using it, please refer to the following resources for additional details.

1. google_generative_ai Dart package documentation

2. Google Generative AI License

Once you have created your API key, you will need to generate a GenerativeModel by specifying the model name of Google AI and passing in your API key. Afterward, you can generate a response using the sendMessage method from the ChatSession, providing the generated prompt. The AI will analyze this information to provide web traffic values, focusing on realistic, data-driven projections rather than simplistic trends.

/// Sends the created prompt to the Google AI.
  /// Converts the response into chart data.
  Future<void> _sendAIChatMessage(String prompt, String apiKey) async {
    try {
      final GenerativeModel model = GenerativeModel(
        model: 'gemini-1.5-flash-latest',
        apiKey: apiKey, // Replace your api key here.
      );
      final ChatSession chat = model.startChat();
      final GenerateContentResponse response = await chat.sendMessage(
        Content.text(prompt),
      );

      _webTrafficData = _convertAIResponseToChartData(response.text);

      setState(() {
        _isLoadingNotifier.value = false;
      });
    } on Object catch (error) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Some error has been occurred $error'),
          ),
        );
      }
    }
  }
Enter fullscreen mode Exit fullscreen mode

Step 3: Convert the generated response into chart data

The response containing the predicted missing data points for web traffic analytics, provided by Google AI, will be in string format. Therefore, we need to parse the response and convert it into chart data according to our existing structures.

/// Sends the created prompt to the Google AI.
/// Converts the response into chart data.
Future<void> _sendAIChatMessage(String prompt, String apiKey) async {
    ......
    _webTrafficData = _convertAIResponseToChartData(response.text);
    ......
}

List<_WebTraffic> _convertAIResponseToChartData(String? data) {
    if (data == null || data.isEmpty) {
      return [];
    }

    int count = 0;
    Color? color;
    final List<_WebTraffic> aiData = [];
    final int webTrafficDataLength = _webTrafficData.length;
    for (final line in data.split('\n')) {
      final parts = line.split(':');
      if (parts.length == 2) {
        try {
          final date = DateFormat('yyyy-MM-dd-HH-m-ss').parse(parts[0].trim());
          final visitors = double.tryParse(parts[1].trim());
          if (visitors != null) {
            final bool isCurrDataNull =
                _webTrafficData[count].trafficCount == null;
            final bool isNextDataNull = count + 1 < webTrafficDataLength &&
                _webTrafficData[count + 1].trafficCount == null;
            color = isCurrDataNull || isNextDataNull
                ? const Color(0xFFD84227)
                : Colors.blue;
            aiData.add(_WebTraffic(date, visitors, color));
            count = count + 1;
          }
        } catch (e) {
          // Handle catch
        }
      }
    }

    return aiData;
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Update the Flutter Spline Chart based on the missing data

Once the web traffic data has been parsed into Chart data, we can stop the circular loading indicator that is currently displayed and update the Flutter Spline Chart with new points by invoking the setState method.

/// Sends the created prompt to Google AI.
/// Converts the response into chart data.
Future<void> _sendAIChatMessage(String prompt, String apiKey) async {
    ......
    _webTrafficData = _convertAIResponseToChartData(response.text);

      setState(() {
        _isLoadingNotifier.value = false;
      });
    ......
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Differentiating the AI-predicted and pre-defined web traffic data

We can customize the appearance of AI-predicted data to distinguish it from the pre-defined web traffic data and make it more visually appealing. This can be achieved by using the pointColorMapper property of the SplineSeries in the SfCartesianChart.

/// Returns the list of Cartesian Line series.
List<SplineSeries<_WebTraffic, DateTime>> _buildSpLineSeries() {
  return <SplineSeries<_WebTraffic, DateTime>>[
    SplineSeries<_WebTraffic, DateTime>(
      ......
      pointColorMapper: (_WebTraffic data, int index) => data.color,
      .....
    ),
  ];
}
Enter fullscreen mode Exit fullscreen mode
List<_WebTraffic> _convertAIResponseToChartData(String? data) {
  ......
  color = isCurrDataNull || isNextDataNull
     ? const Color(0xFFD84227)
     : Colors.blue;
  aiData.add(_WebTraffic(date, visitors, color));
  .....
}
Enter fullscreen mode Exit fullscreen mode

Refer to the following image.

Pre-processing web traffic analytics data using an AI-powered Flutter Spline Chart

Pre-processing web traffic analytics data using an AI-powered Flutter Spline Chart

GitHub reference

For more details, refer to web traffic analytics data pre-processing using the AI-powered smart Flutter Spline Chart GitHub demo.

Conclusion

Thanks for reading! In this blog, we’ve learned how to pre-process web traffic analytics data with the Google AI and Syncfusion Flutter Spline Chart. Give it a try, and leave your feedback in the comment section below.

You can also contact us through our support forums, support portal, or feedback portal. As always, we are happy to assist you!

Related Blogs

Top comments (0)