Fail to run custom widget using fl_chart with FF

Custom Code

I'm trying to integrate inside my FlutterfFow project the fl_chart examples but it keeps failing with an error message related to the 'tooltipBgColor' (attached screenshot)


I'm using the latest code taken from https://github.com/imaNNeo/fl_chart/
dependency: fl_chart: ^0.68.0
Also followed the instructions and examples inside https://pub.dev/packages/fl_chart


The steps I've taken:

  1. Created a custom widget named FLChartBarWidget (same as the class name in my code).

  2. Copied the code example of the bar chart taken from here:
    https://github.com/imaNNeo/fl_chart/blob/main/example/lib/presentation/samples/bar/bar_chart_sample1.dart

  3. The original code example contains 2 url links which failed to be compiled in FF:

    import 'package:fl_chart_app/presentation/resources/app_resources.dart';
    import 'package:fl_chart_app/util/extensions/color_extensions.dart';


    So I've copied their content inside the bottom of my custom widget code (see below).

  4. The code compiled successfully inside the custom code section

  5. Integrated my FLChartBarWidget component inside one of my project pages and everything seemed to work properly (the Mingguan widget at the bottom):

  6. Running the project in test or run mode crashes with the mentioned error message (attached)

What have you tried so far?
  1. I've opened the developer tools in my browser to see if I can get more information but this did'nt really help

  2. I've tried running with both local FF mac desktop app and FF web app

  3. Running it as a clean Flutter code in Android Studio is working fine,, the problem is only with FF.

  • I've noticed that FF is not using the latest Flutter and Dart SDKs.
    Maybe this would be the reason for this?
    I also couldn't find a way to force FF using the latest. Is it possible?

  • The following is the code I'm using for my custom widget:


    // Automatic FlutterFlow imports
    import '/backend/backend.dart';
    import '/backend/schema/structs/index.dart';
    import '/backend/schema/enums/enums.dart';
    import '/backend/supabase/supabase.dart';
    import '/flutter_flow/flutter_flow_theme.dart';
    import '/flutter_flow/flutter_flow_util.dart';
    import '/custom_code/widgets/index.dart'; // Imports other custom widgets
    import '/custom_code/actions/index.dart'; // Imports custom actions
    import '/flutter_flow/custom_functions.dart'; // Imports custom functions
    import 'package:flutter/material.dart';
    // Begin custom widget code
    // DO NOT REMOVE OR MODIFY THE CODE ABOVE!
    
    import 'package:fl_chart/fl_chart.dart';
    import 'dart:math';
    
    //const
    
    class FLChartBarWidget extends StatefulWidget {
      FLChartBarWidget({
        super.key,
        this.width,
        this.height,
      });
    
      final double? width;
      final double? height;
    
      List<Color> get availableColors => const <Color>[
            AppColors.contentColorPurple,
            AppColors.contentColorYellow,
            AppColors.contentColorBlue,
            AppColors.contentColorOrange,
            AppColors.contentColorPink,
            AppColors.contentColorRed,
          ];
    
      final Color barBackgroundColor =
          AppColors.contentColorWhite.withOpacity(1); //0.3
      final Color barColor = AppColors.contentColorWhite;
      final Color touchedBarColor = AppColors.contentColorGreen;
    
      @override
      State<FLChartBarWidget> createState() => _FLChartBarWidgetState();
    }
    
    class _FLChartBarWidgetState extends State<FLChartBarWidget> {
      final Duration animDuration = const Duration(milliseconds: 250);
    
      int touchedIndex = -1;
    
      bool isPlaying = false;
    
      @override
      Widget build(BuildContext context) {
        return AspectRatio(
          aspectRatio: 1,
          child: Stack(
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: <Widget>[
                    const Text(
                      'Mingguan',
                      style: TextStyle(
                        color: AppColors.contentColorGreen,
                        fontSize: 24,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(
                      height: 4,
                    ),
                    Text(
                      'Grafik konsumsi kalori',
                      style: TextStyle(
                        color: AppColors.contentColorGreen,
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(
                      height: 38,
                    ),
                    Expanded(
                      child: Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 8),
                        child: BarChart(
                          isPlaying ? randomData() : mainBarData(),
                          swapAnimationDuration: animDuration,
                        ),
                      ),
                    ),
                    const SizedBox(
                      height: 12,
                    ),
                  ],
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8),
                child: Align(
                  alignment: Alignment.topRight,
                  child: IconButton(
                    icon: Icon(
                      isPlaying ? Icons.pause : Icons.play_arrow,
                      color: AppColors.contentColorGreen,
                    ),
                    onPressed: () {
                      setState(() {
                        isPlaying = !isPlaying;
                        if (isPlaying) {
                          refreshState();
                        }
                      });
                    },
                  ),
                ),
              )
            ],
          ),
        );
      }
    
      BarChartGroupData makeGroupData(
        int x,
        double y, {
        bool isTouched = false,
        Color? barColor,
        double width = 22,
        List<int> showTooltips = const [],
      }) {
        barColor ??= widget.barColor;
        return BarChartGroupData(
          x: x,
          barRods: [
            BarChartRodData(
              toY: isTouched ? y + 1 : y,
              color: isTouched ? widget.touchedBarColor : barColor,
              width: width,
              borderSide: isTouched
                  ? BorderSide(color: widget.touchedBarColor)
                  : const BorderSide(color: Colors.white, width: 0),
              backDrawRodData: BackgroundBarChartRodData(
                show: true,
                toY: 20,
                color: widget.barBackgroundColor,
              ),
            ),
          ],
          showingTooltipIndicators: showTooltips,
        );
      }
    
      List<BarChartGroupData> showingGroups() => List.generate(7, (i) {
            switch (i) {
              case 0:
                return makeGroupData(0, 5, isTouched: i == touchedIndex);
              case 1:
                return makeGroupData(1, 6.5, isTouched: i == touchedIndex);
              case 2:
                return makeGroupData(2, 5, isTouched: i == touchedIndex);
              case 3:
                return makeGroupData(3, 7.5, isTouched: i == touchedIndex);
              case 4:
                return makeGroupData(4, 9, isTouched: i == touchedIndex);
              case 5:
                return makeGroupData(5, 11.5, isTouched: i == touchedIndex);
              case 6:
                return makeGroupData(6, 6.5, isTouched: i == touchedIndex);
              default:
                return throw Error();
            }
          });
    
      BarChartData mainBarData() {
        return BarChartData(
          barTouchData: BarTouchData(
            touchTooltipData: BarTouchTooltipData(
              getTooltipColor: (_) => Colors.blueGrey,
              tooltipHorizontalAlignment: FLHorizontalAlignment.right,
              tooltipMargin: -10,
              getTooltipItem: (group, groupIndex, rod, rodIndex) {
                String weekDay;
                switch (group.x) {
                  case 0:
                    weekDay = 'Monday';
                    break;
                  case 1:
                    weekDay = 'Tuesday';
                    break;
                  case 2:
                    weekDay = 'Wednesday';
                    break;
                  case 3:
                    weekDay = 'Thursday';
                    break;
                  case 4:
                    weekDay = 'Friday';
                    break;
                  case 5:
                    weekDay = 'Saturday';
                    break;
                  case 6:
                    weekDay = 'Sunday';
                    break;
                  default:
                    throw Error();
                }
                return BarTooltipItem(
                  '$weekDay\n',
                  const TextStyle(
                    color: Colors.white,
                    fontWeight: FontWeight.bold,
                    fontSize: 18,
                  ),
                  children: <TextSpan>[
                    TextSpan(
                      text: (rod.toY - 1).toString(),
                      style: const TextStyle(
                        color: Colors.white, //widget.touchedBarColor,
                        fontSize: 16,
                        fontWeight: FontWeight.w500,
                      ),
                    ),
                  ],
                );
              },
            ),
            touchCallback: (FlTouchEvent event, barTouchResponse) {
              setState(() {
                if (!event.isInterestedForInteractions ||
                    barTouchResponse == null ||
                    barTouchResponse.spot == null) {
                  touchedIndex = -1;
                  return;
                }
                touchedIndex = barTouchResponse.spot!.touchedBarGroupIndex;
              });
            },
          ),
          titlesData: FlTitlesData(
            show: true,
            rightTitles: const AxisTitles(
              sideTitles: SideTitles(showTitles: false),
            ),
            topTitles: const AxisTitles(
              sideTitles: SideTitles(showTitles: false),
            ),
            bottomTitles: AxisTitles(
              sideTitles: SideTitles(
                showTitles: true,
                getTitlesWidget: getTitles,
                reservedSize: 38,
              ),
            ),
            leftTitles: const AxisTitles(
              sideTitles: SideTitles(
                showTitles: false,
              ),
            ),
          ),
          borderData: FlBorderData(
            show: false,
          ),
          barGroups: showingGroups(),
          gridData: const FlGridData(show: false),
        );
      }
    
      Widget getTitles(double value, TitleMeta meta) {
        const style = TextStyle(
          color: Colors.white,
          fontWeight: FontWeight.bold,
          fontSize: 14,
        );
        Widget text;
        switch (value.toInt()) {
          case 0:
            text = const Text('M', style: style);
            break;
          case 1:
            text = const Text('T', style: style);
            break;
          case 2:
            text = const Text('W', style: style);
            break;
          case 3:
            text = const Text('T', style: style);
            break;
          case 4:
            text = const Text('F', style: style);
            break;
          case 5:
            text = const Text('S', style: style);
            break;
          case 6:
            text = const Text('S', style: style);
            break;
          default:
            text = const Text('', style: style);
            break;
        }
        return SideTitleWidget(
          axisSide: meta.axisSide,
          space: 16,
          child: text,
        );
      }
    
      BarChartData randomData() {
        return BarChartData(
          barTouchData: BarTouchData(
            enabled: false,
          ),
          titlesData: FlTitlesData(
            show: true,
            bottomTitles: AxisTitles(
              sideTitles: SideTitles(
                showTitles: true,
                getTitlesWidget: getTitles,
                reservedSize: 38,
              ),
            ),
            leftTitles: const AxisTitles(
              sideTitles: SideTitles(
                showTitles: false,
              ),
            ),
            topTitles: const AxisTitles(
              sideTitles: SideTitles(
                showTitles: false,
              ),
            ),
            rightTitles: const AxisTitles(
              sideTitles: SideTitles(
                showTitles: false,
              ),
            ),
          ),
          borderData: FlBorderData(
            show: false,
          ),
          barGroups: List.generate(7, (i) {
            switch (i) {
              case 0:
                return makeGroupData(
                  0,
                  Random().nextInt(15).toDouble() + 6,
                  barColor: widget.availableColors[
                      Random().nextInt(widget.availableColors.length)],
                );
              case 1:
                return makeGroupData(
                  1,
                  Random().nextInt(15).toDouble() + 6,
                  barColor: widget.availableColors[
                      Random().nextInt(widget.availableColors.length)],
                );
              case 2:
                return makeGroupData(
                  2,
                  Random().nextInt(15).toDouble() + 6,
                  barColor: widget.availableColors[
                      Random().nextInt(widget.availableColors.length)],
                );
              case 3:
                return makeGroupData(
                  3,
                  Random().nextInt(15).toDouble() + 6,
                  barColor: widget.availableColors[
                      Random().nextInt(widget.availableColors.length)],
                );
              case 4:
                return makeGroupData(
                  4,
                  Random().nextInt(15).toDouble() + 6,
                  barColor: widget.availableColors[
                      Random().nextInt(widget.availableColors.length)],
                );
              case 5:
                return makeGroupData(
                  5,
                  Random().nextInt(15).toDouble() + 6,
                  barColor: widget.availableColors[
                      Random().nextInt(widget.availableColors.length)],
                );
              case 6:
                return makeGroupData(
                  6,
                  Random().nextInt(15).toDouble() + 6,
                  barColor: widget.availableColors[
                      Random().nextInt(widget.availableColors.length)],
                );
              default:
                return throw Error();
            }
          }),
          gridData: const FlGridData(show: false),
        );
      }
    
      Future<dynamic> refreshState() async {
        setState(() {});
        await Future<dynamic>.delayed(
          animDuration + const Duration(milliseconds: 50),
        );
        if (isPlaying) {
          await refreshState();
        }
      }
    }
    
    class AppColors {
      static const Color primary = contentColorCyan;
      static const Color menuBackground = Color(0xFF090912);
      static const Color itemsBackground = Color(0xFF1B2339);
      static const Color pageBackground = Color(0xFF282E45);
      static const Color mainTextColor1 = Colors.white;
      static const Color mainTextColor2 = Colors.white70;
      static const Color mainTextColor3 = Colors.white38;
      static const Color mainGridLineColor = Colors.white10;
      static const Color borderColor = Colors.white54;
      static const Color gridLinesColor = Color(0x11FFFFFF);
    
      static const Color contentColorBlack = Colors.black;
      static const Color contentColorWhite = Colors.white;
      static const Color contentColorBlue = Color(0xFF2196F3);
      static const Color contentColorYellow = Color(0xFFFFC300);
      static const Color contentColorOrange = Color(0xFFFF683B);
      static const Color contentColorGreen = Color(0xFF3BFF49);
      static const Color contentColorPurple = Color(0xFF6E1BFF);
      static const Color contentColorPink = Color(0xFFFF3AF2);
      static const Color contentColorRed = Color(0xFFE80054);
      static const Color contentColorCyan = Color(0xFF50E4FF);
    }
    
    class AppAssets {
      static String getChartIcon(ChartType type) {
        switch (type) {
          case ChartType.line:
            return 'assets/icons/ic_line_chart.svg';
          case ChartType.bar:
            return 'assets/icons/ic_bar_chart.svg';
          case ChartType.pie:
            return 'assets/icons/ic_pie_chart.svg';
          case ChartType.scatter:
            return 'assets/icons/ic_scatter_chart.svg';
          case ChartType.radar:
            return 'assets/icons/ic_radar_chart.svg';
        }
      }
    
      static const flChartLogoIcon = 'assets/icons/fl_chart_logo_icon.png';
      static const flChartLogoText = 'assets/icons/fl_chart_logo_text.svg';
    }
    
    class AppDimens {
      static const double menuMaxNeededWidth = 304;
      static const double menuRowHeight = 74;
      static const double menuIconSize = 32;
      static const double menuDocumentationIconSize = 44;
      static const double menuTextSize = 20;
    
      static const double chartBoxMinWidth = 350;
    
      static const double defaultRadius = 8;
      static const double chartSamplesSpace = 32.0;
      static const double chartSamplesMinWidth = 350;
    }
    
    class AppTexts {
      static const appName = 'FL Chart App';
    }
    
    enum ChartType { line, bar, pie, scatter, radar }
    
    extension ChartTypeExtension on ChartType {
      String get displayName => '$simpleName Chart';
    
      String get simpleName => switch (this) {
            ChartType.line => 'Line',
            ChartType.bar => 'Bar',
            ChartType.pie => 'Pie',
            ChartType.scatter => 'Scatter',
            ChartType.radar => 'Radar',
          };
    
      String get documentationUrl => Urls.getChartDocumentationUrl(this);
    
      String get assetIcon => AppAssets.getChartIcon(this);
    }
    
    class Urls {
      static const flChartUrl = 'https://flchart.dev';
      static const flChartGithubUrl = 'https://github.com/imaNNeo/fl_chart';
    
      static String get aboutUrl => '$flChartUrl/about';
    
      static String getChartSourceCodeUrl(ChartType chartType, int sampleNumber) {
        final chartDir = chartType.name.toLowerCase();
        return 'https://github.com/imaNNeo/fl_chart/blob/main/example/lib/presentation/samples/$chartDir/${chartDir}_chart_sample$sampleNumber.dart';
      }
    
      static String getChartDocumentationUrl(ChartType chartType) {
        final chartDir = chartType.name.toLowerCase();
        return 'https://github.com/imaNNeo/fl_chart/blob/main/repo_files/documentations/${chartDir}_chart.md';
      }
    
      static String getVersionReleaseUrl(String version) =>
          '$flChartGithubUrl/releases/tag/$version';
    }

Did you check FlutterFlow's Documentation for this topic?
Yes
1
5 replies