我正在尝试使用需要显示正值和负值的条形图,但我面临三个问题:
这是条形图部分的代码:
import 'package:expense_tracker/Charts/bar_data.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
class BarGraph extends StatelessWidget {
final double? maxY;
final double monAmount;
final double tueAmount;
final double wedAmount;
final double thuAmount;
final double friAmount;
final double satAmount;
final double sunAmount;
const BarGraph({
super.key,
required this.maxY,
required this.monAmount,
required this.tueAmount,
required this.wedAmount,
required this.thuAmount,
required this.friAmount,
required this.satAmount,
required this.sunAmount,
});
@override
Widget build(BuildContext context) {
//initialize bar data
BarData myData = BarData(
monAmount: monAmount,
tueAmount: tueAmount,
wedAmount: wedAmount,
thuAmount: thuAmount,
friAmount: friAmount,
satAmount: satAmount,
sunAmount: sunAmount,
);
myData.initializeBarData();
return BarChart(
BarChartData(
maxY: maxY,
minY: 0,
gridData: FlGridData(show: false),
borderData: FlBorderData(show: false),
titlesData: FlTitlesData(
show: true,
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: getBottomTitles,
),
)),
barGroups: myData.barData
.map(
(data) => BarChartGroupData(
x: data.x,
barRods: [
BarChartRodData(
toY: data.y,
color: Colors.blueGrey,
width: 25,
borderRadius: BorderRadius.circular(5),
backDrawRodData: BackgroundBarChartRodData(
show: true,
fromY: double.parse('-$maxY'),
toY: maxY,
color: Colors.blueGrey[300],
),
),
],
),
)
.toList(),
),
swapAnimationCurve: Curves.bounceIn,
swapAnimationDuration: const Duration(milliseconds: 300),
);
}
}
Widget getBottomTitles(double value, TitleMeta meta) {
TextStyle style = TextStyle(
color: Colors.blueGrey[700], fontWeight: FontWeight.bold, fontSize: 14);
Widget text;
switch (value.toInt()) {
case 0:
text = Text(
'Mon',
style: style,
);
break;
case 1:
text = Text(
'Tue',
style: style,
);
break;
case 2:
text = Text(
'Wed',
style: style,
);
break;
case 3:
text = Text(
'Thu',
style: style,
);
break;
case 4:
text = Text(
'Fri',
style: style,
);
break;
case 5:
text = Text(
'Sat',
style: style,
);
break;
case 6:
text = Text(
'Sun',
style: style,
);
break;
default:
text = Text(
'',
style: style,
);
break;
}
return SideTitleWidget(
axisSide: meta.axisSide,
child: text,
);
}
这是代码的可重现版本。
IndividualBar
、BarData
和BarGraph
在原始代码中都位于不同的dart文件中:
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
flex: 1,
child: Card(
color: Colors.blueGrey[200],
child: const SizedBox(
height: 100,
child: BarGraph(
maxY: 50,
monAmount: 20,
tueAmount: 25,
wedAmount: 43,
thuAmount: -10,
friAmount: -36,
satAmount: 16,
sunAmount: -10,
),
),
),
),
Expanded(
flex: 1,
child: Card(
color: Colors.blueGrey[200],
child: SizedBox(
width: MediaQuery.of(context).size.width,
),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class IndividualBar {
final int x; //position on x axis
final double y; //amount
IndividualBar({required this.x, required this.y});
}
class BarData {
final double monAmount;
final double tueAmount;
final double wedAmount;
final double thuAmount;
final double friAmount;
final double satAmount;
final double sunAmount;
BarData({
required this.monAmount,
required this.tueAmount,
required this.wedAmount,
required this.thuAmount,
required this.friAmount,
required this.satAmount,
required this.sunAmount,
});
//make a list from Individual bar data
List<IndividualBar> barData = [];
//initialize bar data
void initializeBarData() {
barData = [
//mon
IndividualBar(x: 0, y: monAmount),
//tue
IndividualBar(x: 1, y: tueAmount),
//wed
IndividualBar(x: 2, y: wedAmount),
//thu
IndividualBar(x: 3, y: thuAmount),
//fri
IndividualBar(x: 4, y: friAmount),
//sat
IndividualBar(x: 5, y: satAmount),
//sun
IndividualBar(x: 6, y: sunAmount),
];
}
}
class BarGraph extends StatelessWidget {
final double? maxY;
final double monAmount;
final double tueAmount;
final double wedAmount;
final double thuAmount;
final double friAmount;
final double satAmount;
final double sunAmount;
const BarGraph({
super.key,
required this.maxY,
required this.monAmount,
required this.tueAmount,
required this.wedAmount,
required this.thuAmount,
required this.friAmount,
required this.satAmount,
required this.sunAmount,
});
@override
Widget build(BuildContext context) {
//initialize bar data
BarData myData = BarData(
monAmount: monAmount,
tueAmount: tueAmount,
wedAmount: wedAmount,
thuAmount: thuAmount,
friAmount: friAmount,
satAmount: satAmount,
sunAmount: sunAmount,
);
myData.initializeBarData();
return Padding(
padding: const EdgeInsets.all(8.0),
child: BarChart(
BarChartData(
//alignment: BarChartAlignment.spaceAround,
maxY: maxY,
minY: double.parse('-$maxY'),
gridData: FlGridData(show: false),
borderData: FlBorderData(show: false),
titlesData: FlTitlesData(
show: true,
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: getBottomTitles,
),
)),
barGroups: myData.barData
.map(
(data) => BarChartGroupData(
x: data.x,
barRods: [
BarChartRodData(
toY: data.y,
color: data.y > 0
? const Color.fromARGB(255, 0, 212, 138)
: const Color.fromARGB(255, 231, 0, 0),
width: 40,
borderRadius: BorderRadius.circular(5),
borderSide: BorderSide(
width: 2.0,
color: data.y > 0
? const Color.fromARGB(255, 28, 169, 119)
: const Color.fromARGB(255, 171, 23, 23),
),
backDrawRodData: BackgroundBarChartRodData(
show: true,
fromY: double.parse('-$maxY'),
toY: maxY,
color: Colors.blueGrey[300],
),
),
],
),
)
.toList(),
),
swapAnimationCurve: Curves.bounceIn,
swapAnimationDuration: const Duration(milliseconds: 300),
),
);
}
}
Widget getBottomTitles(double value, TitleMeta meta) {
TextStyle style = TextStyle(
color: Colors.blueGrey[700], fontWeight: FontWeight.bold, fontSize: 14);
Widget text;
switch (value.toInt()) {
case 0:
text = Text(
'Mon',
style: style,
);
break;
case 1:
text = Text(
'Tue',
style: style,
);
break;
case 2:
text = Text(
'Wed',
style: style,
);
break;
case 3:
text = Text(
'Thu',
style: style,
);
break;
case 4:
text = Text(
'Fri',
style: style,
);
break;
case 5:
text = Text(
'Sat',
style: style,
);
break;
case 6:
text = Text(
'Sun',
style: style,
);
break;
default:
text = Text(
'',
style: style,
);
break;
}
return SideTitleWidget(
axisSide: meta.axisSide,
child: text,
);
}
从这一切中可以很清楚地看出,我对 Flutter 非常陌生......所以我将依靠这里出色的人们的帮助! :)
提前致谢!
如果您还没有找到这个问题的答案,我可能有一些有用的信息。
例如:
Card(
child: AspectRatio(
aspectRatio: 16/9, // HERE YOU CAN SET YOUR CUSTOM ASPECT RATIO LIKE 4/3, etc.
child: BarGraph(
...
),
),
),
widget 有两种类型:StatefulWidget、StatelessWidget。
总而言之,无状态小部件是那些不需要更改其内部值的小部件,例如不更改其值的标签。
有状态小部件是那些需要更新其中某些值的小部件,例如文本字段或具有多个文本字段和按钮的完整登录页面。
在您的情况下,如果您希望 BarChart 类是可变的,例如在添加随机化图表值的按钮时,我建议使用 StatefulWidget,这取决于您实际如何使用它。
隐式动画是指当您更改小部件内部的值时,小部件会自动产生动画,例如:AnimatedOpacity,在此小部件中,您只需动态更改不透明度属性,它就会自动产生动画。
Tween 动画稍微复杂一点,它使用 AnimationController 和 Tween 值以编程方式控制小部件值,一个示例可能是进度条,您希望在按下按钮时从 0 到 100 进行动画处理,但您希望逐渐增加值,因此它看起来是动画的。
在您的场景中,fl_charts 包使用隐式动画来对图表的更改进行动画处理,因此您需要使用变量或变量列表来更改条形值,例如,您可以使用 dart 随机库以及整数列表来实现此目的:
import 'dart:math';
List<Int> values = [];
void onButtonPressed() {
Random random = new Random();
values = List.generate(10, () => random.nextInt(100)); // THIS REPLACES CURRENT LIST FOR 10 RANDOMIZED INTEGERS FROM 0 to 99
}
然后只需使用该整数列表将它们分配给您的图表值:
BarChart(
BarChartData(
//alignment: BarChartAlignment.spaceAround,
maxY: maxY,
minY: double.parse('-$maxY'),
gridData: FlGridData(show: false),
borderData: FlBorderData(show: false),
titlesData: FlTitlesData(
show: true,
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: getBottomTitles,
),
)),
barGroups: values
.map(
(value) => BarChartGroupData(
x: value.index, // NOTE THAT THIS IS AN EXAMPLE AND YOU WOULD NEED TO ADAPT IT TO YOUR REAL CODE
barRods: [
BarChartRodData(
toY: value, // NOW "VALUE" IS A RANDOMIZED VALUE FROM 0 TO 99, BUT WITH REAL VALUES THE COLOR CODE WILL WORK CORRECTLY FOR NEGATIVES AND POSITIVES.
color: value > 0
? const Color.fromARGB(255, 0, 212, 138)
: const Color.fromARGB(255, 231, 0, 0),
width: 40,
borderRadius: BorderRadius.circular(5),
borderSide: BorderSide(
width: 2.0,
color: value > 0
? const Color.fromARGB(255, 28, 169, 119)
: const Color.fromARGB(255, 171, 23, 23),
),
backDrawRodData: BackgroundBarChartRodData(
show: true,
fromY: double.parse('-$maxY'),
toY: maxY,
color: Colors.blueGrey[300],
),
),
],
),
)
.toList(),
),
swapAnimationCurve: Curves.bounceIn,
swapAnimationDuration: const Duration(milliseconds: 300),
),
现在图表栏上使用了列表“值”,并且当您按下按钮时值会发生变化,然后 Flutter 将自动为您的图表添加动画效果。
我希望通过这些解释和例子你可以实现你想要的。
问候。