import 'package:dotted_dashed_line/dotted_dashed_line.dart'; import 'package:smartfit_app_mobile/common_widget/round_button.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:simple_animation_progress_bar/simple_animation_progress_bar.dart'; import 'package:simple_circular_progress_bar/simple_circular_progress_bar.dart'; import 'package:smartfit_app_mobile/view/home/activity_tracker.dart'; import 'package:smartfit_app_mobile/view/home/notification_view.dart'; import '../../common/colo_extension.dart'; //import 'activity_tracker_view.dart'; //import 'finished_workout_view.dart'; //import 'notification_view.dart'; class HomeView extends StatefulWidget { const HomeView({super.key}); @override State createState() => _HomeViewState(); } class _HomeViewState extends State { List lastWorkoutArr = [ { "name": "Full Body Workout", "image": "assets/img/Workout1.png", "kcal": "180", "time": "20", "progress": 0.3 }, { "name": "Lower Body Workout", "image": "assets/img/Workout2.png", "kcal": "200", "time": "30", "progress": 0.4 }, { "name": "Ab Workout", "image": "assets/img/Workout3.png", "kcal": "300", "time": "40", "progress": 0.7 }, ]; List showingTooltipOnSpots = [0]; List allSpots = [FlSpot(0, 20)]; List waterArr = [ {"title": "6am - 8am", "subtitle": "600ml"}, {"title": "9am - 11am", "subtitle": "500ml"}, {"title": "11am - 2pm", "subtitle": "1000ml"}, {"title": "2pm - 4pm", "subtitle": "700ml"}, {"title": "4pm - now", "subtitle": "900ml"}, ]; @override Widget build(BuildContext context) { var media = MediaQuery.of(context).size; final lineBarsData = [ LineChartBarData( showingIndicators: showingTooltipOnSpots, spots: allSpots, isCurved: false, barWidth: 2, belowBarData: BarAreaData( show: true, gradient: LinearGradient(colors: [ TColor.secondaryColor1.withOpacity(0.4), TColor.secondaryColor2.withOpacity(0.1), ], begin: Alignment.topCenter, end: Alignment.bottomCenter), ), dotData: FlDotData(show: false), gradient: LinearGradient( colors: TColor.secondaryG, ), ), ]; final tooltipsOnBar = lineBarsData[0]; return Scaffold( backgroundColor: TColor.white, body: SingleChildScrollView( child: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Bienvenue,", style: TextStyle(color: TColor.gray, fontSize: 12), ), Text( "Benjelloun Othmane", style: TextStyle( color: TColor.black, fontSize: 20, fontWeight: FontWeight.w700), ), ], ), IconButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => const NotificationView(), ), ); }, icon: Image.asset( "assets/img/notification_active.png", width: 25, height: 25, fit: BoxFit.fitHeight, )) ], ), SizedBox( height: media.width * 0.05, ), Container( height: media.width * 0.4, decoration: BoxDecoration( gradient: LinearGradient(colors: TColor.primaryG), borderRadius: BorderRadius.circular(media.width * 0.075)), child: Stack(alignment: Alignment.center, children: [ Image.asset( "assets/img/bg_dots.png", height: media.width * 0.4, width: double.maxFinite, fit: BoxFit.fitHeight, ), Padding( padding: const EdgeInsets.symmetric( vertical: 25, horizontal: 25), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Graph 1", style: TextStyle( color: TColor.white, fontSize: 14, fontWeight: FontWeight.w700), ), Text( "Sous titre 1", style: TextStyle( color: TColor.white.withOpacity(0.7), fontSize: 12), ), SizedBox( height: media.width * 0.05, ), SizedBox( width: 120, height: 35, child: RoundButton( title: "Voir plus", type: RoundButtonType.bgSGradient, fontSize: 12, fontWeight: FontWeight.w400, onPressed: () {})) ], ), AspectRatio( aspectRatio: 1, child: PieChart( PieChartData( pieTouchData: PieTouchData( touchCallback: (FlTouchEvent event, pieTouchResponse) {}, ), startDegreeOffset: 250, borderData: FlBorderData( show: false, ), sectionsSpace: 1, centerSpaceRadius: 0, sections: showingSections(), ), ), ), ], ), ) ]), ), SizedBox( height: media.width * 0.05, ), Container( padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15), decoration: BoxDecoration( color: TColor.primaryColor2.withOpacity(0.3), borderRadius: BorderRadius.circular(15), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Suivi d'activité", style: TextStyle( color: TColor.black, fontSize: 14, fontWeight: FontWeight.w700), ), SizedBox( width: 70, height: 25, child: RoundButton( title: "Voir", type: RoundButtonType.bgGradient, fontSize: 12, fontWeight: FontWeight.w400, onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => const ActivityTrackerView(), ), ); }, ), ) ], ), ), SizedBox( height: media.width * 0.05, ), Text( "Status d'activité", style: TextStyle( color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700), ), SizedBox( height: media.width * 0.02, ), ClipRRect( borderRadius: BorderRadius.circular(25), child: Container( height: media.width * 0.4, width: double.maxFinite, decoration: BoxDecoration( color: TColor.primaryColor2.withOpacity(0.3), borderRadius: BorderRadius.circular(25), ), child: Stack( alignment: Alignment.topLeft, children: [ Padding( padding: const EdgeInsets.symmetric( vertical: 20, horizontal: 20), child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Graph 2 ( rhythme cardiaque )", style: TextStyle( color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700), ), ShaderMask( blendMode: BlendMode.srcIn, shaderCallback: (bounds) { return LinearGradient( colors: TColor.primaryG, begin: Alignment.centerLeft, end: Alignment.centerRight) .createShader(Rect.fromLTRB( 0, 0, bounds.width, bounds.height)); }, child: Text( "78 BPM", style: TextStyle( color: TColor.primaryColor1.withOpacity(0.7), fontWeight: FontWeight.w700, fontSize: 18), ), ), ], ), ), LineChart( LineChartData( showingTooltipIndicators: showingTooltipOnSpots.map((index) { return ShowingTooltipIndicators([ LineBarSpot( tooltipsOnBar, lineBarsData.indexOf(tooltipsOnBar), tooltipsOnBar.spots[index], ), ]); }).toList(), lineTouchData: LineTouchData( enabled: true, handleBuiltInTouches: false, touchCallback: (FlTouchEvent event, LineTouchResponse? response) { if (response == null || response.lineBarSpots == null) { return; } if (event is FlTapUpEvent) { final spotIndex = response.lineBarSpots!.first.spotIndex; showingTooltipOnSpots.clear(); setState(() { showingTooltipOnSpots.add(spotIndex); }); } }, mouseCursorResolver: (FlTouchEvent event, LineTouchResponse? response) { if (response == null || response.lineBarSpots == null) { return SystemMouseCursors.basic; } return SystemMouseCursors.click; }, getTouchedSpotIndicator: (LineChartBarData barData, List spotIndexes) { return spotIndexes.map((index) { return TouchedSpotIndicatorData( FlLine( color: TColor.secondaryColor1, ), FlDotData( show: true, getDotPainter: (spot, percent, barData, index) => FlDotCirclePainter( radius: 3, color: Colors.white, strokeWidth: 3, strokeColor: TColor.secondaryColor1, ), ), ); }).toList(); }, touchTooltipData: LineTouchTooltipData( tooltipBgColor: TColor.secondaryColor1, tooltipRoundedRadius: 20, getTooltipItems: (List lineBarsSpot) { return lineBarsSpot.map((lineBarSpot) { return LineTooltipItem( "il y a ${lineBarSpot.x.toInt()} minutes", const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ); }).toList(); }, ), ), lineBarsData: lineBarsData, minY: 0, maxY: 130, titlesData: FlTitlesData( show: false, ), gridData: FlGridData(show: false), borderData: FlBorderData( show: true, border: Border.all( color: Colors.transparent, ), ), ), ) ], ), ), ), SizedBox( height: media.width * 0.05, ), Row( children: [ Expanded( child: Container( height: media.width * 0.95, padding: const EdgeInsets.symmetric( vertical: 25, horizontal: 20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(25), boxShadow: const [ BoxShadow(color: Colors.black12, blurRadius: 2) ]), child: Row( children: [ SimpleAnimationProgressBar( height: media.width * 0.85, width: media.width * 0.07, backgroundColor: Colors.grey.shade100, foregrondColor: Colors.purple, ratio: 0.5, direction: Axis.vertical, curve: Curves.fastLinearToSlowEaseIn, duration: const Duration(seconds: 3), borderRadius: BorderRadius.circular(15), gradientColor: LinearGradient( colors: TColor.primaryG, begin: Alignment.bottomCenter, end: Alignment.topCenter), ), const SizedBox( width: 10, ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Graph 3", style: TextStyle( color: TColor.black, fontSize: 12, fontWeight: FontWeight.w700), ), ShaderMask( blendMode: BlendMode.srcIn, shaderCallback: (bounds) { return LinearGradient( colors: TColor.primaryG, begin: Alignment.centerLeft, end: Alignment.centerRight) .createShader(Rect.fromLTRB( 0, 0, bounds.width, bounds.height)); }, child: Text( "ex : objectif", style: TextStyle( color: TColor.white.withOpacity(0.7), fontWeight: FontWeight.w700, fontSize: 14), ), ), const SizedBox( height: 10, ), Text( "Mis à jour en temps réel", style: TextStyle( color: TColor.gray, fontSize: 12, ), ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: waterArr.map((wObj) { var isLast = wObj == waterArr.last; return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( margin: const EdgeInsets.symmetric( vertical: 4), width: 10, height: 10, decoration: BoxDecoration( color: TColor.secondaryColor1 .withOpacity(0.5), borderRadius: BorderRadius.circular(5), ), ), if (!isLast) DottedDashedLine( height: media.width * 0.078, width: 0, dashColor: TColor .secondaryColor1 .withOpacity(0.5), axis: Axis.vertical) ], ), const SizedBox( width: 10, ), Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( wObj["title"].toString(), style: TextStyle( color: TColor.gray, fontSize: 10, ), ), ShaderMask( blendMode: BlendMode.srcIn, shaderCallback: (bounds) { return LinearGradient( colors: TColor.secondaryG, begin: Alignment .centerLeft, end: Alignment .centerRight) .createShader(Rect.fromLTRB( 0, 0, bounds.width, bounds.height)); }, child: Text( wObj["subtitle"].toString(), style: TextStyle( color: TColor.white .withOpacity(0.7), fontSize: 12), ), ), ], ) ], ); }).toList(), ) ], )) ], ), ), ), SizedBox( width: media.width * 0.05, ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( width: double.maxFinite, height: media.width * 0.45, padding: const EdgeInsets.symmetric( vertical: 25, horizontal: 20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(25), boxShadow: const [ BoxShadow(color: Colors.black12, blurRadius: 2) ]), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Graph 4", style: TextStyle( color: TColor.black, fontSize: 12, fontWeight: FontWeight.w700), ), ShaderMask( blendMode: BlendMode.srcIn, shaderCallback: (bounds) { return LinearGradient( colors: TColor.primaryG, begin: Alignment.centerLeft, end: Alignment.centerRight) .createShader(Rect.fromLTRB( 0, 0, bounds.width, bounds.height)); }, child: Text( "durée", style: TextStyle( color: TColor.white.withOpacity(0.7), fontWeight: FontWeight.w700, fontSize: 14), ), ), const Spacer(), Image.asset("assets/img/sleep_graph.png", width: double.maxFinite, height: 80, fit: BoxFit.fitWidth) ]), ), SizedBox( height: media.width * 0.05, ), Container( width: double.maxFinite, height: media.width * 0.45, padding: const EdgeInsets.symmetric( vertical: 25, horizontal: 20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(25), boxShadow: const [ BoxShadow(color: Colors.black12, blurRadius: 2) ]), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Calories", style: TextStyle( color: TColor.black, fontSize: 12, fontWeight: FontWeight.w700), ), ShaderMask( blendMode: BlendMode.srcIn, shaderCallback: (bounds) { return LinearGradient( colors: TColor.primaryG, begin: Alignment.centerLeft, end: Alignment.centerRight) .createShader(Rect.fromLTRB( 0, 0, bounds.width, bounds.height)); }, child: Text( "760 kCal", style: TextStyle( color: TColor.white.withOpacity(0.7), fontWeight: FontWeight.w700, fontSize: 14), ), ), const Spacer(), Container( alignment: Alignment.center, child: SizedBox( width: media.width * 0.2, height: media.width * 0.2, child: Stack( alignment: Alignment.center, children: [ Container( width: media.width * 0.15, height: media.width * 0.15, alignment: Alignment.center, decoration: BoxDecoration( gradient: LinearGradient( colors: TColor.primaryG), borderRadius: BorderRadius.circular( media.width * 0.075), ), child: FittedBox( child: Text( "230kCal\nrestantes", textAlign: TextAlign.center, style: TextStyle( color: TColor.white, fontSize: 11), ), ), ), SimpleCircularProgressBar( progressStrokeWidth: 10, backStrokeWidth: 10, progressColors: TColor.primaryG, backColor: Colors.grey.shade100, valueNotifier: ValueNotifier(50), startAngle: -180, ), ], ), ), ) ]), ), ], )) ], ), SizedBox( height: media.width * 0.1, ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "graph 5", style: TextStyle( color: TColor.black, fontSize: 16, fontWeight: FontWeight.w700), ), Container( height: 30, padding: const EdgeInsets.symmetric(horizontal: 8), decoration: BoxDecoration( gradient: LinearGradient(colors: TColor.primaryG), borderRadius: BorderRadius.circular(15), ), child: DropdownButtonHideUnderline( child: DropdownButton( items: ["Semaine", "Mois"] .map((name) => DropdownMenuItem( value: name, child: Text( name, style: TextStyle( color: TColor.gray, fontSize: 14), ), )) .toList(), onChanged: (value) {}, icon: Icon(Icons.expand_more, color: TColor.white), hint: Text( "Semaine", textAlign: TextAlign.center, style: TextStyle(color: TColor.white, fontSize: 12), ), ), )), ], ), SizedBox( height: media.width * 0.05, ), Container( padding: const EdgeInsets.only(left: 15), height: media.width * 0.5, width: double.maxFinite, child: LineChart( LineChartData( showingTooltipIndicators: showingTooltipOnSpots.map((index) { return ShowingTooltipIndicators([ LineBarSpot( tooltipsOnBar, lineBarsData.indexOf(tooltipsOnBar), tooltipsOnBar.spots[index], ), ]); }).toList(), lineTouchData: LineTouchData( enabled: true, handleBuiltInTouches: false, touchCallback: (FlTouchEvent event, LineTouchResponse? response) { if (response == null || response.lineBarSpots == null) { return; } if (event is FlTapUpEvent) { final spotIndex = response.lineBarSpots!.first.spotIndex; showingTooltipOnSpots.clear(); setState(() { showingTooltipOnSpots.add(spotIndex); }); } }, mouseCursorResolver: (FlTouchEvent event, LineTouchResponse? response) { if (response == null || response.lineBarSpots == null) { return SystemMouseCursors.basic; } return SystemMouseCursors.click; }, getTouchedSpotIndicator: (LineChartBarData barData, List spotIndexes) { return spotIndexes.map((index) { return TouchedSpotIndicatorData( FlLine( color: Colors.transparent, ), FlDotData( show: true, getDotPainter: (spot, percent, barData, index) => FlDotCirclePainter( radius: 3, color: Colors.white, strokeWidth: 3, strokeColor: TColor.secondaryColor1, ), ), ); }).toList(); }, touchTooltipData: LineTouchTooltipData( tooltipBgColor: TColor.secondaryColor1, tooltipRoundedRadius: 20, getTooltipItems: (List lineBarsSpot) { return lineBarsSpot.map((lineBarSpot) { return LineTooltipItem( "il y a ${lineBarSpot.x.toInt()} minutes", const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ); }).toList(); }, ), ), lineBarsData: lineBarsData1, minY: -0.5, maxY: 110, titlesData: FlTitlesData( show: true, leftTitles: AxisTitles(), topTitles: AxisTitles(), bottomTitles: AxisTitles( sideTitles: bottomTitles, ), rightTitles: AxisTitles( sideTitles: rightTitles, )), gridData: FlGridData( show: true, drawHorizontalLine: true, horizontalInterval: 25, drawVerticalLine: false, getDrawingHorizontalLine: (value) { return FlLine( color: TColor.gray.withOpacity(0.15), strokeWidth: 2, ); }, ), borderData: FlBorderData( show: true, border: Border.all( color: Colors.transparent, ), ), ), )), SizedBox( height: media.width * 0.05, ), SizedBox( height: media.width * 0.1, ), ], ), ), ), ), ); } void updateChartData(List newData) { setState(() { allSpots = newData; }); } List showingSections() { return List.generate( 2, (i) { var color0 = TColor.secondaryColor1; switch (i) { case 0: return PieChartSectionData( color: color0, value: 33, title: '', radius: 55, titlePositionPercentageOffset: 0.55, badgeWidget: const Text( "20,1", style: TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w700), )); case 1: return PieChartSectionData( color: Colors.white, value: 75, title: '', radius: 45, titlePositionPercentageOffset: 0.55, ); default: throw Error(); } }, ); } LineTouchData get lineTouchData1 => LineTouchData( handleBuiltInTouches: true, touchTooltipData: LineTouchTooltipData( tooltipBgColor: Colors.blueGrey.withOpacity(0.8), ), ); List get lineBarsData1 => [ lineChartBarData1_1, lineChartBarData1_2, ]; LineChartBarData get lineChartBarData1_1 => LineChartBarData( isCurved: true, gradient: LinearGradient(colors: [ TColor.primaryColor2.withOpacity(0.5), TColor.primaryColor1.withOpacity(0.5), ]), barWidth: 4, isStrokeCapRound: true, dotData: FlDotData(show: false), belowBarData: BarAreaData(show: false), spots: const [ FlSpot(1, 35), FlSpot(2, 70), FlSpot(3, 40), FlSpot(4, 80), FlSpot(5, 25), FlSpot(6, 70), FlSpot(7, 35), ], ); LineChartBarData get lineChartBarData1_2 => LineChartBarData( isCurved: true, gradient: LinearGradient(colors: [ TColor.secondaryColor2.withOpacity(0.5), TColor.secondaryColor1.withOpacity(0.5), ]), barWidth: 2, isStrokeCapRound: true, dotData: FlDotData(show: false), belowBarData: BarAreaData( show: false, ), spots: const [ FlSpot(1, 80), FlSpot(2, 50), FlSpot(3, 90), FlSpot(4, 40), FlSpot(5, 80), FlSpot(6, 35), FlSpot(7, 60), ], ); SideTitles get rightTitles => SideTitles( getTitlesWidget: rightTitleWidgets, showTitles: true, interval: 20, reservedSize: 40, ); Widget rightTitleWidgets(double value, TitleMeta meta) { String text; switch (value.toInt()) { case 0: text = '0%'; break; case 20: text = '20%'; break; case 40: text = '40%'; break; case 60: text = '60%'; break; case 80: text = '80%'; break; case 100: text = '100%'; break; default: return Container(); } return Text(text, style: TextStyle( color: TColor.gray, fontSize: 12, ), textAlign: TextAlign.center); } SideTitles get bottomTitles => SideTitles( showTitles: true, reservedSize: 32, interval: 1, getTitlesWidget: bottomTitleWidgets, ); Widget bottomTitleWidgets(double value, TitleMeta meta) { var style = TextStyle( color: TColor.gray, fontSize: 12, ); Widget text; switch (value.toInt()) { case 1: text = Text('Dim', style: style); break; case 2: text = Text('Lun', style: style); break; case 3: text = Text('Mar', style: style); break; case 4: text = Text('Mer', style: style); break; case 5: text = Text('Jeu', style: style); break; case 6: text = Text('Ven', style: style); break; case 7: text = Text('Sam', style: style); break; default: text = Text('', style: style); break; } return SideTitleWidget( axisSide: meta.axisSide, space: 10, child: text, ); } }