0

我想更新图表的数据,但它不起作用。我使用了我在 YouTube 视频上看到的图表,它使用了 fl_chart 包。这是 Youtube 视频的完整代码:https ://github.com/JohannesMilke/fl_bar_chart_example 。我想我必须在某个地方使用 setState ,但我不知道在哪里......我尝试了一些选项,但都失败了,所以除了问你之外我没有别的办法。weekChartDataGlobal 是我希望能够更改的变量,因此我的目标是使图表动态化。我只是或多或少地将整个代码放入了问题中,因为我不确切知道您需要什么来回答我的问题。如果您需要更多代码,请写信给我。提前感谢您帮助我解决这个大问题。

class BarChartWidget extends StatefulWidget {
  @override
  _BarChartWidgetState createState() => _BarChartWidgetState();
}

class _BarChartWidgetState extends State<BarChartWidget> {
  final double barWidth = 22;

  @override
  Widget build(BuildContext context) {
    return BarChart(
      BarChartData(
        alignment: BarChartAlignment.center,
        maxY: 20,
        minY: 0,
        groupsSpace: MediaQuery.of(context).size.width * 0.05,
        barTouchData: BarTouchData(enabled: true),
        titlesData: FlTitlesData(
          bottomTitles: BarTitles.getTopBottomTitles(),
          leftTitles: BarTitles.getSideTitles(),
        ),
        gridData: FlGridData(
          checkToShowHorizontalLine: (value) => value % BarData.interval == 0,
          getDrawingHorizontalLine: (value) {
            if (value == 0) {
              return FlLine(
                color: const Color(0xff363753),
                strokeWidth: 3,
              );
            } else {
              return FlLine(
                color: const Color(0xff2a2747),
                strokeWidth: 0.8,
              );
            }
          },
        ),
        barGroups: BarData.barData
            .map(
              (data) => BarChartGroupData(
                x: data.id,
                barRods: [
                  BarChartRodData(
                      y: data.y,
                      width: barWidth,
                      colors: [data.color],
                      borderRadius: BorderRadius.only(
                        topLeft: Radius.circular(10),
                        topRight: Radius.circular(10),
                      )),
                ],
              ),
            )
            .toList(),
      ),
    );
  }
}

    class BarTitles {
      static SideTitles getTopBottomTitles() => SideTitles(
            showTitles: true,
            getTextStyles: (value) =>
                const TextStyle(color: Colors.white, fontSize: 12),
            margin: 0,
            getTitles: (double id) => BarData.barData
                .firstWhere((element) => element.id == id.toInt())
                .name,
          );
    
      static SideTitles getSideTitles() => SideTitles(
            showTitles: true,
            getTextStyles: (value) =>
                const TextStyle(color: Colors.white, fontSize: 12),
            rotateAngle: 90,
            interval: BarData.interval.toDouble(),
            margin: 0,
            reservedSize: 30,
            getTitles: (double value) => value == 0 ? '0' : '${value.toInt()}',
          );
    }
class BarData {
  static int interval = 5;

  static List<Data> barData = [
    Data(
      id: 0,
      name: 'Mon',
      y: weekChartDataGlobal[0]['dayTime'].toDouble(),
      color: Color(0xff19bfff),
    ),
    Data(
      name: 'Tue',
      id: 1,
      y: weekChartDataGlobal[1]['dayTime'].toDouble(),
      color: Color(0xffff4d94),
    ),
    Data(
      name: 'Wed',
      id: 2,
      y: weekChartDataGlobal[2]['dayTime'].toDouble(),
      color: Color(0xff2bdb90),
    ),
    Data(
      name: 'Thu',
      id: 3,
      y: weekChartDataGlobal[3]['dayTime'].toDouble(),
      color: Color(0xffffdd80),
    ),
    Data(
      name: 'Fri',
      id: 4,
      y: weekChartDataGlobal[4]['dayTime'].toDouble(),
      color: Color(0xff2bdb90),
    ),
    Data(
      name: 'Sat',
      id: 5,
      y: weekChartDataGlobal[5]['dayTime'].toDouble(),
      color: Color(0xffffdd80),
    ),
    Data(
      name: 'Sun',
      id: 6,
      y: weekChartDataGlobal[6]['dayTime'].toDouble(),
      color: Color(0xffff4d94),
    ),
  ];
}

class Data {
  // for ordering in the graph
  final int id;
  final String name;
  final double y;
  final Color color;

  const Data({
    @required this.name,
    @required this.id,
    @required this.y,
    @required this.color,
  });
}
4

1 回答 1

0

好吧,我更新此图表的唯一方法是将图表放入列表变量中,将此列表放入构建方法中,并在每次我想更新图表时使用函数替换图表。让我试着解释一下。按照步骤以了解我在这里做什么并耐心等待。要立即测试它,只需复制代码,删除我的评论并运行它。不要忘记对 pubspec.yaml 中的 fl_chart 依赖项使用 pub get 并使用import 'package:fl_chart/fl_chart.dart' 导入库;

// STEP 1 - create the global variables - this is to adapt your code to this approach. 
//Put all these variables outside and above any function of your BarChartWidget class.

late BuildContext sameContext; //we will initialize this variable later, its the build's method context variable

late _BarChartWidgetState thisState; //we will initialize this variable later, its the class State variable, wich contains the setState method

double theChanges = 0; // this is a placeholder for your weekChartDataGlobal variable since i don't know what is this weekChartDataGlobal but i know its the info that will be displayed and updated in the Y axis of the chart so i replaced it with this double variable to ilustrate this approach

final double barWidth = 22; // your barWidth variable made global


// STEP 4 - here is the method to get a new chart. It's the exact same code of our theChart list 
//but once we throw the old BarChart widget away we need to get another and this new one will call all 
//its methods again and be updated with the new Y values from theChanges in new Data objects. 
//So this method just return the BarChart widget.
Widget getTheChart(){
  return BarChart(
    BarChartData(
      alignment: BarChartAlignment.center,
      maxY: 20,
      minY: 0,
      groupsSpace: MediaQuery.of(sameContext).size.width * 0.05,
      barTouchData: BarTouchData(enabled: true),
      titlesData: FlTitlesData(
        bottomTitles: BarTitles.getTopBottomTitles(),
        leftTitles: BarTitles.getSideTitles(),
      ),
      gridData: FlGridData(
        checkToShowHorizontalLine: (value) => value % BarData.interval == 0,
        getDrawingHorizontalLine: (value) {
          if (value == 0) {
            return FlLine(
              color: const Color(0xff363753),
              strokeWidth: 3,
            );
          } else {
            return FlLine(
              color: const Color(0xff2a2747),
              strokeWidth: 0.8,
            );
          }
        },
      ),
      barGroups: BarData.barData
          .map(
            (data) => BarChartGroupData(
          x: data.id,
          barRods: [
            BarChartRodData(
                y: data.y,
                width: barWidth,
                colors: [data.color],
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(10),
                  topRight: Radius.circular(10),
                )),
          ],
        ),
      ).toList(),
    ),
  );
}




// STEP 2 - Here i putted all the widgets of your page inside a list variable called theChart, 
//this is to make things easier to control, so we can update the BarChart widget easily. 
//In this list i'm putting your BarChart widget and a Button widget below it for you to be able to see 
//the chart being updated. I created this list too as a global variable. 
//Note that since this list is being declared outside the build method, we are using 
//sameContext variable instead of context and thisState.setState((){}) instead of setState((){}).
//As you can see in the list, the BarChart widget is placed at 0 and the Container holding the 
//MaterialButton is placed at 1.

List<Widget> theChart = <Widget>[
  BarChart(
    BarChartData(
      alignment: BarChartAlignment.center,
      maxY: 20,
      minY: 0,
      //sameContext variable being used here
      groupsSpace: MediaQuery.of(sameContext).size.width * 0.05,
      barTouchData: BarTouchData(enabled: true),
      titlesData: FlTitlesData(
        bottomTitles: BarTitles.getTopBottomTitles(),
        leftTitles: BarTitles.getSideTitles(),
      ),
      gridData: FlGridData(
        checkToShowHorizontalLine: (value) => value % BarData.interval == 0,
        getDrawingHorizontalLine: (value) {
          if (value == 0) {
            return FlLine(
              color: const Color(0xff363753),
              strokeWidth: 3,
            );
          } else {
            return FlLine(
              color: const Color(0xff2a2747),
              strokeWidth: 0.8,
            );
          }
        },
      ),
      barGroups: BarData.barData
          .map(
            (data) => BarChartGroupData(
          x: data.id,
          barRods: [
            BarChartRodData(
                y: data.y,
                width: barWidth,
                colors: [data.color],
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(10),
                  topRight: Radius.circular(10),
                )),
          ],
        ),
      ).toList(),
    ),
  ),
  //this is the button widget, click to increase Y (theChanges variable) by 5,
//long press to decrease Y (theChanges variable)  by 5
  Container(
    margin: EdgeInsets.fromLTRB(0, 10, 0, 0),
    child: MaterialButton(
      color: Colors.lightBlue,
      textColor: Colors.white,
      child: Text("Press me!"),
      onPressed: () {
//when you press the button we enter this method. Here we are outside the build method wich contains 
//the context and the state variables so we need to use our state variable to call the setState 
//method. This is the key point of the whole thing of updating the chart. We change the variable 
//holding the Y value in the chart, here i'm using my theChanges variable but in your code it's that weekChartDataGlobal. 
//Then we call the function BarData.updateData() (check step 3) to update the BarData static list variable wich contains the actual 
//objects holding the Y values in the chart, and then we REPLACE THE BarChart WIDGET. That's right we throw 
//the old widget away and replace it with a new one with the new values. Widgets atributes are final, we 
//can't change them once they're settled so we need to replace them, the sooner you accept it the sooner you'll understand all this.
        thisState.setState((){
          theChanges += 5;
          BarData.updateData();
          theChart.removeAt(0);
          theChart.insert(0, getTheChart()); // check step 4
        });
      },
      onLongPress: (){
        thisState.setState((){
          theChanges -= 5;
          BarData.updateData();
          theChart.removeAt(0);
          theChart.insert(0, getTheChart());
        });
      },
    ),
  ),
].toList();

class BarChartWidget extends StatefulWidget {
  @override
  _BarChartWidgetState createState() => _BarChartWidgetState();
}

class _BarChartWidgetState extends State<BarChartWidget> {

  @override
  Widget build(BuildContext context) {
    // at this method your page will actually be built, here is where all the action will happen 
//indeed so we
    sameContext = context; // initialize our global context variable
    thisState = this;  // initialize our global State variable

    //here we put theChart list variable inside a ListView and the ListView inside a container. 
//The build method will get each widget inside the list and place it in order
    return Container(
      child: ListView.builder(
        itemCount: theChart.length,
        itemBuilder: (context, index){
          return theChart[index];
        },
      ),
    );
  }
}

class BarTitles {
  static SideTitles getTopBottomTitles() => SideTitles(
    showTitles: true,
    getTextStyles: (value) =>
    const TextStyle(color: Colors.white, fontSize: 12),
    margin: 0,
    getTitles: (double id) => BarData.barData
        .firstWhere((element) => element.id == id.toInt())
        .name,
  );

  static SideTitles getSideTitles() => SideTitles(
    showTitles: true,
    getTextStyles: (value) =>
    const TextStyle(color: Colors.white, fontSize: 12),
    rotateAngle: 90,
    interval: BarData.interval.toDouble(),
    margin: 0,
    reservedSize: 30,
    getTitles: (double value) => value == 0 ? '0' : '${value.toInt()}',
  );
}

// STEP 3 here i replaced your weekChartDataGlobal with my theChanges variable to ilustrate the 
//update and created the updateData() method.
class BarData {
  static int interval = 5;

  static List<Data> barData = [
    Data(
      id: 0,
      name: 'Mon',
      //y: weekChartDataGlobal[0]['dayTime'].toDouble(),
      y: theChanges,
      color: Color(0xff19bfff),
    ),
    Data(
      name: 'Tue',
      id: 1,
      //y: weekChartDataGlobal[1]['dayTime'].toDouble(),
      y: theChanges,
      color: Color(0xffff4d94),
    ),
    Data(
      name: 'Wed',
      id: 2,
      //y: weekChartDataGlobal[2]['dayTime'].toDouble(),
      y: theChanges,
      color: Color(0xff2bdb90),
    ),
    Data(
      name: 'Thu',
      id: 3,
      //y: weekChartDataGlobal[3]['dayTime'].toDouble(),
      y: theChanges,
      color: Color(0xffffdd80),
    ),
    Data(
      name: 'Fri',
      id: 4,
      //y: weekChartDataGlobal[4]['dayTime'].toDouble(),
      y: theChanges,
      color: Color(0xff2bdb90),
    ),
    Data(
      name: 'Sat',
      id: 5,
      //y: weekChartDataGlobal[5]['dayTime'].toDouble(),
      y: theChanges,
      color: Color(0xffffdd80),
    ),
    Data(
      name: 'Sun',
      id: 6,
      //y: weekChartDataGlobal[6]['dayTime'].toDouble(),
      y: theChanges,
      color: Color(0xffff4d94),
    ),
  ];

//this is another key point. Note that here i'm just clearing the whole static list and filling it 
//again with the Data objects. I just copied the code above to create the static list and 
//pasted down there. Why? Widgets attributes are final, their Y parameter won't change even if 
//you update the theChanges variable so you need to throw them all away and place new ones 
//with the new value of theChanges variable.
  static void updateData(){
    barData.clear();
    barData = [
      Data(
        id: 0,
        name: 'Mon',
        //y: weekChartDataGlobal[0]['dayTime'].toDouble(),
        y: theChanges,
        color: Color(0xff19bfff),
      ),
      Data(
        name: 'Tue',
        id: 1,
        //y: weekChartDataGlobal[1]['dayTime'].toDouble(),
        y: theChanges,
        color: Color(0xffff4d94),
      ),
      Data(
        name: 'Wed',
        id: 2,
        //y: weekChartDataGlobal[2]['dayTime'].toDouble(),
        y: theChanges,
        color: Color(0xff2bdb90),
      ),
      Data(
        name: 'Thu',
        id: 3,
        //y: weekChartDataGlobal[3]['dayTime'].toDouble(),
        y: theChanges,
        color: Color(0xffffdd80),
      ),
      Data(
        name: 'Fri',
        id: 4,
        //y: weekChartDataGlobal[4]['dayTime'].toDouble(),
        y: theChanges,
        color: Color(0xff2bdb90),
      ),
      Data(
        name: 'Sat',
        id: 5,
        //y: weekChartDataGlobal[5]['dayTime'].toDouble(),
        y: theChanges,
        color: Color(0xffffdd80),
      ),
      Data(
        name: 'Sun',
        id: 6,
        //y: weekChartDataGlobal[6]['dayTime'].toDouble(),
        y: theChanges,
        color: Color(0xffff4d94),
      ),
    ];
  }
}

class Data {
  // for ordering in the graph
  final int id;
  final String name;
  final double y;
  final Color color;

  const Data({
    required this.name,
    required this.id,
    required this.y,
    required this.color,
  });
}
于 2022-01-19T02:20:07.807 回答