Horizontal bar charts in Flutter - flutter

I'm trying to achieve a horizontal bar chart in flutter. I came across these packages fl_charts and charts_flutter. but I need to customise the looks to match with the following image I attached.
If any champ can support me in giving me steps to how to achieve this or an exemplary code will be so much helpful.
Thank you so much in advance.

Use LayoutBuilder to know the possible width, and IntrinsicWidth to calculate the place of title and numbers, but at the same time don't shrink the long title text.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: SafeArea(
child: MyHomePage(),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_title('Food Weight(grams)'),
Padding(
padding: const EdgeInsets.only(left: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ChartLine(title: 'Fat', number: 1800, rate: 1),
ChartLine(title: 'Protein', number: 600, rate: 0.4)
],
),
),
_title('Ratios'),
Padding(
padding: const EdgeInsets.only(left: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ChartLine(
title: 'Calculum/Phosporous ratio = 2:1',
rate: 0.5,
),
ChartLine(
title: 'Omega3/6 ratio = 1:4',
rate: 0.4,
),
],
),
),
],
),
);
}
Widget _title(String title) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0, top: 4),
child: Text(
title,
style: TextStyle(fontSize: 18),
),
);
}
}
class ChartLine extends StatelessWidget {
const ChartLine({
Key key,
#required this.rate,
#required this.title,
this.number,
}) : assert(title != null),
assert(rate != null),
assert(rate > 0),
assert(rate <= 1),
super(key: key);
final double rate;
final String title;
final int number;
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
final lineWidget = constraints.maxWidth * rate;
return Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
constraints: BoxConstraints(minWidth: lineWidget),
child: IntrinsicWidth(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
),
),
if (number != null)
Text(
number.toString(),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
Container(
color: Colors.blue,
height: 60,
width: lineWidget,
),
],
),
);
});
}
}

You can use the Syncfusion's Flutter charts package, as I am using now for my application. Using Syncfusion's SfCartesianChart widget, render the bar chart along with data labels and by changing the data label's default position your output can be achieved. This widget works on Android, iOS, and web platforms.
Package- https://pub.dev/packages/syncfusion_flutter_charts
Features- https://www.syncfusion.com/flutter-widgets/flutter-charts
Documentation- https://help.syncfusion.com/flutter/cartesian-charts/overview

Related

How can I create a ListView correctly?

I'm not able to create a Listview in Flutter because of when I create a Listview of widgets the screen stays empty, it's something like that 1
This is the Code that I wrote and returns a list view:
import 'package:dietapp/pages/homepage.dart';
import 'package:dietapp/pages/list.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:dietapp/pages/profile.dart';
import 'package:dietapp/pages/createReg.dart';
import 'package:percent_indicator/percent_indicator.dart';
void main() {}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
const SafeArea(child: TopBar()),
const Align(
alignment: Alignment.topLeft,
child: Padding(
padding: EdgeInsets.only(left: 25, bottom: 20),
child: Text('Seguiment Diari', style: TextStyle(fontSize: 20)),
)),
Align(alignment: Alignment.center, child: TypesListView()),
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const CreateReg()));
},
label: const Text('Crear'),
icon: const Icon(Icons.add),
),
);
}
}
class TopBar extends StatelessWidget {
const TopBar({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(25.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
"Dietapp",
style: TextStyle(
color: Colors.black, fontSize: 30, fontWeight: FontWeight.bold),
),
],
),
);
}
}
class TotalLabel extends StatefulWidget {
final String typeOf;
final String subtitle;
final Function() onPressed;
final double fillBar;
const TotalLabel(
{required this.typeOf,
required this.subtitle,
required this.onPressed,
required this.fillBar,
Key? key})
: super(key: key);
#override
State<TotalLabel> createState() => _TotalLabelState();
}
class _TotalLabelState extends State<TotalLabel> {
Color getColor(double fillBar) {
if (fillBar < 0.5) {
return Colors.orange;
} else {
return Colors.green;
}
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onPressed,
child: Container(
width: 350,
height: 125,
padding: const EdgeInsets.all(15.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.5),
boxShadow: [
BoxShadow(
offset: const Offset(10, 20),
blurRadius: 10,
spreadRadius: 0,
color: Colors.grey.withOpacity(.05)),
],
),
child: Column(
children: [
Text(widget.typeOf,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,
)),
const SizedBox(
height: 5,
),
Text(
widget.subtitle,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.grey,
fontWeight: FontWeight.normal,
fontSize: 12),
),
const SizedBox(
height: 10,
),
const Spacer(),
LinearPercentIndicator(
width: 300,
lineHeight: 10,
barRadius: const Radius.circular(50),
backgroundColor: Colors.black12,
progressColor: getColor(widget.fillBar),
percent: widget.fillBar,
),
const Spacer()
],
),
),
);
}
}
class TypesListView extends StatelessWidget {
const TypesListView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(8),
children: <Widget>[
TotalLabel(
typeOf: 'Proteines',
subtitle: 'Range',
onPressed: () {},
fillBar: 0.2),
],
);
}
}
When I run the code, the error view is the following:
I have also tried to use a Stateless widget returning a list view but didn't worked.
Thanks you so much :)
The following is an example of how to use a ListView. Note that I created a MaterialApp since ListView is a Material Widget. You can replace ListViewExample with your own Widget containing a ListView.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ListView Example',
home: ListViewExample(),
);
}
}
class ListViewExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(8),
children: <Widget>[
Text('Text Widget 1'),
Text('Text Widget 2'),
Text('Text Widget 3'),
],
);
}
}
ListView.builder(
itemCount: 5
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(10),
child: Text("Some text $index")
),
);
}),
More about listview

Evade flutter overflow when using row

[this is on desktop,not web or android]
I have following code, which creates a split screen (one side of screen has shortcuts to content and other side shows that content) it also acts somewhat as drawer since it can be closed or opened.
the left pane i reduce in one of two cases, when user clicks arrow button, or when the window side is less then 700. but if i try to take right edge of the screen and move very fast to reduce the size of whole window, the row will overflow and i will get an error.
So far i tryed using several things to achive this layout :
Using Flexible and setting fit and flex properties, (same issue as now since parent of them was again a row)
way that i am showing you now.
Also i could try to limit the size of screen lets say min value is 640 x 480 and that would also somewhat prevent error from happening.
I am just wondering is there another way to achieve this layout .
I also tryed several packages from pub.dev and ALL of them have same overflow error in their implementation .(easy_sidemenu,side_menu_navigation, etc)
my code
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: MyHomePage(
title: "My home",
),
);
}
}
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
final List<bool> isSelected = [false, false, false];
class _MyHomePageState extends ConsumerState<MyHomePage> {
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
elevation: 0,
toolbarHeight: 35,
centerTitle: true,
title: Text(widget.title),
),
body: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 500),
width: width > 700 ? 250 : 50,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 5),
children: [
TextButton(
onPressed: () {},
child: Row(
children: [
Icon(
Icons.home,
color: Theme.of(context).colorScheme.secondary,
size: 30,
),
const Flexible(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text(
"Home",
maxLines: 1,
style:
TextStyle(overflow: TextOverflow.clip),
),
),
),
],
),
),
]),
),
const SizedBox(
height: 5,
),
TextButton(
onPressed: () {},
child: Row(
children: const [
Icon(
Icons.settings,
size: 30,
),
Flexible(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text(
"Settings",
maxLines: 1,
style: TextStyle(overflow: TextOverflow.clip),
),
),
),
],
),
),
],
),
),
IconButton(onPressed: () {}, icon: const Icon(Icons.arrow_back)),
Expanded(
child: Container(
color: Colors.red,
)),
],
),
);
}
}

No errors in Flutter but custom widgets not showing

I am trying to make a ResuseableCard custom widget and IconContent custom widget.
Problem: Reuseable card are empty. IconContent is not showing inside ReusueableCard
What I have tried:
I also failed at creating a cardChild property within Reuseable Card that has a Column containing the icon, sized box and label. I did not see any errors when I tried this but the cards remained empty!
What has worked: When everything is spelled out within ReuseableCard (no separate IconContent widget or cardChild property) it works. But then all the cards look the same.
Another thing that works is to separately code each card. I don't want repeated code. The tutorial I follow works so why doesn't my code?
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
const bottomContainerHeight = 80.0;
const activeCardColor = Color(0xFF1D1E33);
const bottomContainerColor = Color(0xFFEB1555);
const labelColor = Color(0xFF76FF03);
class InputPage extends StatefulWidget {
#override
_ InputPageState createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xFF0A0E21),
title: Text('BMI CALCULATOR'),
centerTitle: true,
),
body: Column(
children: [
Expanded(
child: Row(children: [
Expanded(
child: ReuseableCard(
colour: activeCardColor,
cardChild: IconContent(FontAwesomeIcons.venus, 'Female'),
),
),
Expanded(
child: ReuseableCard(
colour: activeCardColor,
cardChild: Column(children: [
Icon(FontAwesomeIcons.mars, size: 80.0),
SizedBox(height: 15.0),
Text('Male',
style: TextStyle(
fontSize: 18.0, color: (Color(0xFF8d8E98)))),
]),
)),
])),
Expanded(
child: ReuseableCard(colour: activeCardColor),
),
Expanded(
child: Row(
children: [
Expanded(
child: ReuseableCard(colour: activeCardColor),
),
Expanded(
child: ReuseableCard(colour: activeCardColor),
)
],
),
),
Container(
color: bottomContainerColor,
margin: EdgeInsets.only(top: 10.0),
width: double.infinity,
height: 80.0,
)
],
));
}
}
class ReuseableCard extends StatelessWidget {
ReuseableCard({#required colour, cardChild});
Color? colour;
Widget? IconContent;
#override
Widget build(BuildContext context) {
return Container(
child: IconContent,
margin: EdgeInsets.all(15.0),
decoration: BoxDecoration(
color: Color(0xFF1D1E33),
borderRadius: BorderRadius.circular(10.0)));
}
}
class IconContent extends StatelessWidget {
IconData? data;
String label = 'label';
IconContent(data, label);
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(data, size: 80.0),
SizedBox(height: 15.0),
Text(label,
style: TextStyle(
fontSize: 18.0,
color: labelColor,
))
]);
}
}
You have not defined cardChild properly and are not calling it in the ReuseableCard. I have made all the fields as required in the below code.
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
const bottomContainerHeight = 80.0;
const activeCardColor = Color(0xFF1D1E33);
const bottomContainerColor = Color(0xFFEB1555);
const labelColor = Color(0xFF76FF03);
class InputPage extends StatefulWidget {
#override
_ InputPageState createState() => _InputPageState(); }
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xFF0A0E21),
title: Text('BMI CALCULATOR'),
centerTitle: true,
),
body: Column(
children: [
Expanded(
child: Row(children: [
Expanded(
child: ReuseableCard(
colour: activeCardColor,
cardChild: IconContent(data:FontAwesomeIcons.venus,label: 'Female'),
),
),
Expanded(
child: ReuseableCard(
colour: activeCardColor,
cardChild: Column(children: [
Icon(FontAwesomeIcons.mars, size: 80.0),
SizedBox(height: 15.0),
Text('Male',
style: TextStyle(
fontSize: 18.0, color: (Color(0xFF8d8E98)))),
]),
)),
])),
Expanded(
child: ReuseableCard(colour: activeCardColor),
),
Expanded(
child: Row(
children: [
Expanded(
child: ReuseableCard(colour: activeCardColor),
),
Expanded(
child: ReuseableCard(colour: activeCardColor),
)
],
),
),
Container(
color: bottomContainerColor,
margin: EdgeInsets.only(top: 10.0),
width: double.infinity,
height: 80.0,
)
],
));
}
}
class ReuseableCard extends StatelessWidget {
ReuseableCard({#required this.colour,#required this.cardChild});
Color? colour;
Widget? cardChild;//cardChild defined here so the ReuseableCard can use it
#override
Widget build(BuildContext context) {
return Container(
child: cardChild,//called here
margin: EdgeInsets.all(15.0),
decoration: BoxDecoration(
color: Color(0xFF1D1E33),
borderRadius: BorderRadius.circular(10.0)));
}
}
class IconContent extends StatelessWidget {
IconData? data;
String? label;
IconContent({#required this.data, #required this.label});
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(data, size: 80.0),
SizedBox(height: 15.0),
Text(label??"label",
style: TextStyle(
fontSize: 18.0,
color: labelColor,
))
]);
}
}
I think this will do.
class ReuseableCard extends StatelessWidget {
ReuseableCard({Key? key,required this.colour, this.cardChild}) : super(key: key);
final Color colour;
final Widget? cardChild;
#override
Widget build(BuildContext context) {
return Container(
child: cardChild?? const SizedBox(),
margin: EdgeInsets.all(15.0),
decoration: BoxDecoration(
color: Color(0xFF1D1E33),
borderRadius: BorderRadius.circular(10.0)));
}
}
when you use
required
you don't have to say it can be null, also don't user widgets directly if it can be null.
if you want more help with your design, provide us your UI design what you trying to create.

Flutter text and stack widget spaced evenly

I am trying to create a status indicator in flutter with text label on left side and stack widget on right side, I manage to arrange them using Row but the width of each widgets is not separated evenly and the stack max width overflows with the container.
Here is my code so far.
import 'package:flutter/material.dart';
class CustomIndicatorBar extends StatelessWidget {
const CustomIndicatorBar({
Key key,
#required this.percent,
#required this.title,
}) : super(key: key);
final double percent;
final String title;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final double maxBarWidth = constraints.maxWidth;
double barWidth = percent * maxBarWidth;
if (barWidth < 0) {
barWidth = 0;
}
return Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
Stack(
children: [
Container(
height: 20.0,
decoration: const BoxDecoration(
color: Color(0xFF555454),
),
),
Container(
height: 20.0,
width: barWidth,
decoration: const BoxDecoration(
color: Color(0xFFFA9F1D),
),
)
],
)
],
),
);
},
),
);
}
}
Here is the output
What I want to achieve is to space them out evenly and stack width will not overflow from the container.
Please try my solution:
The result:
Full code:
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
title: 'Demo',
home: FirstRoute(),
));
}
class FirstRoute extends StatefulWidget {
const FirstRoute({Key? key}) : super(key: key);
#override
State<FirstRoute> createState() => _FirstRouteState();
}
class _FirstRouteState extends State<FirstRoute> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('First Route'),
),
body: Column(
children: const [
SizedBox(height: 16),
CustomIndicatorBar(
title: 'Mood',
percent: 0.5,
),
SizedBox(height: 16),
CustomIndicatorBar(
title: 'Hunger',
percent: 0.7,
),
],
),
);
}
}
class CustomIndicatorBar extends StatelessWidget {
const CustomIndicatorBar({
Key? key,
required this.percent,
required this.title,
}) : super(key: key);
final double percent;
final String title;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
Expanded(
child: Stack(
children: [
Container(
height: 20.0,
decoration: const BoxDecoration(
color: Color(0xFF555454),
),
),
LayoutBuilder(builder: (context, constraints) {
return Container(
height: 20.0,
width: constraints.maxWidth * percent,
decoration: const BoxDecoration(
color: Color(0xFFFA9F1D),
),
);
})
],
),
)
],
),
);
}
}

How can I implement an animation on this change of page in flutter? using changenotifierprovider

I am trying to implement an animation which makes the second page come from right and the first page come from left. like animation in MaterialPageRoute. This is my code. Do I have to use pageroute to get an animation like it? Since there's a bottom nav bar and a custom appbar in my app. I wouldn't like to use pageroute for this. any suggestions would be appreciated.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'selection.dart';
void main() {
runApp(MultiProvider(
providers: [ChangeNotifierProvider.value(value: Selection())],
child: MyApp()));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController animationController;
#override
void initState() {
animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
);
super.initState();
}
#override
void dispose() {
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Size deviceSize = MediaQuery.of(context).size;
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 8, right: 8),
child: Container(
height: 32,
width: deviceSize.width,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.notifications_none_outlined),
Expanded(
child: Text(
"address",
textAlign: TextAlign.center,
)),
Icon(Icons.search_rounded),
],
),
),
),
Container(
height: 48,
width: deviceSize.width,
child: Column(
children: [
Container(
height: 42,
width: deviceSize.width,
child: Row(
children: [
Expanded(
child: TextButton(
child: Text("idle",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: context.watch<Selection>().selected
? Color(0xFF616161)
: Color(0xFF2ac1bc))),
onPressed: () {
context.read<Selection>().setFalse();
animationController.reverse();
},
),
),
Expanded(
child: TextButton(
child: Text("selected",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: context.watch<Selection>().selected
? Color(0xFF2ac1bc)
: Color(0xFF616161))),
onPressed: () {
context.read<Selection>().setTrue();
animationController.forward();
},
),
)
],
),
),
Expanded(
child: Stack(alignment: Alignment.topLeft, children: [
PositionedTransition(
rect: RelativeRectTween(
begin: RelativeRect.fromLTRB(
0, 0, deviceSize.width / 2, 0),
end: RelativeRect.fromLTRB(
deviceSize.width / 2, 0, 0, 0),
).animate(animationController),
child: Container(
width: deviceSize.width / 2,
height: 1,
color: Colors.pinkAccent)),
]),
),
],
),
),
Expanded(
child: context.watch<Selection>().selected
? Container(
color: Color(0xFF2ac1bc),
)
: Container(color: Color(0xFF616161)),
)
],
));
}
}
try to use PageView for the content part, use PageController to change index of PageView will get the effect of 'the second page come from right and the first page come from left'