Related
I want to change color of line in stepper, Look at the below picture
my try here :
Theme(
data: ThemeData(canvasColor: whiteColor),
child: Stepper(
type: StepperType.horizontal,
steps: getSteps(),
currentStep: currentStep,
elevation: 2,
),
),
Please help to continue my code.
Let me be short and clear.
TO CHANGE STEPPER LINE COLOR
Unfortunately, the Flutter code for the Stepper does not support the change of the Stepper line color (_buildLine) and more.
So I created a custom stepper called CusStepper that uses CusStep
NOTE: This CusStepper has the same properties and behaves like the normal Stepper with the only difference of lineColor property.
Steps on how to use the CusStepper
Create a new .dart file to store the CusStepper code
Paste the code below for the CusStepper in the file.
Import it to the file you want to use it in.
Finally change the stepper line color with the lineColor params. on the CusStepper
CusStepper code to paste in the .dart file created in step 1
// THIS IS A CUSTOM STEPPER THAT ADDS LINE COLOR
import 'package:flutter/material.dart';
enum CusStepState {
indexed,
editing,
complete,
disabled,
error,
}
enum CusStepperType {
vertical,
horizontal,
}
#immutable
class ControlsDetails {
const ControlsDetails({
required this.currentStep,
required this.stepIndex,
this.onStepCancel,
this.onStepContinue,
});
final int currentStep;
final int stepIndex;
final VoidCallback? onStepContinue;
final VoidCallback? onStepCancel;
bool get isActive => currentStep == stepIndex;
}
typedef ControlsWidgetBuilder = Widget Function(
BuildContext context, ControlsDetails details);
const TextStyle _kStepStyle = TextStyle(
fontSize: 12.0,
color: Colors.white,
);
const Color _kErrorLight = Colors.red;
final Color _kErrorDark = Colors.red.shade400;
const Color _kCircleActiveLight = Colors.white;
const Color _kCircleActiveDark = Colors.black87;
const Color _kDisabledLight = Colors.black38;
const Color _kDisabledDark = Colors.white38;
const double _kStepSize = 24.0;
const double _kTriangleHeight = _kStepSize * 0.866025;
#immutable
class CusStep {
const CusStep({
required this.title,
this.subtitle,
required this.content,
this.state = CusStepState.indexed,
this.isActive = false,
this.label,
});
final Widget title;
final Widget? subtitle;
final Widget content;
final CusStepState state;
final bool isActive;
final Widget? label;
}
class CusStepper extends StatefulWidget {
const CusStepper({
super.key,
required this.steps,
this.physics,
this.type = CusStepperType.vertical,
this.currentStep = 0,
this.onStepTapped,
this.onStepContinue,
this.onStepCancel,
this.controlsBuilder,
this.elevation,
this.margin,
this.lineColor = Colors.grey,
}) : assert(0 <= currentStep && currentStep < steps.length);
final List<CusStep> steps;
final ScrollPhysics? physics;
final CusStepperType type;
final int currentStep;
final ValueChanged<int>? onStepTapped;
final VoidCallback? onStepContinue;
final VoidCallback? onStepCancel;
final ControlsWidgetBuilder? controlsBuilder;
final double? elevation;
final EdgeInsetsGeometry? margin;
final Color lineColor;
#override
State<CusStepper> createState() => _CusStepperState();
}
class _CusStepperState extends State<CusStepper> with TickerProviderStateMixin {
late List<GlobalKey> _keys;
final Map<int, CusStepState> _oldStates = <int, CusStepState>{};
#override
void initState() {
super.initState();
_keys = List<GlobalKey>.generate(
widget.steps.length,
(int i) => GlobalKey(),
);
for (int i = 0; i < widget.steps.length; i += 1) {
_oldStates[i] = widget.steps[i].state;
}
}
#override
void didUpdateWidget(CusStepper oldWidget) {
super.didUpdateWidget(oldWidget);
assert(widget.steps.length == oldWidget.steps.length);
for (int i = 0; i < oldWidget.steps.length; i += 1) {
_oldStates[i] = oldWidget.steps[i].state;
}
}
bool _isFirst(int index) {
return index == 0;
}
bool _isLast(int index) {
return widget.steps.length - 1 == index;
}
bool _isCurrent(int index) {
return widget.currentStep == index;
}
bool _isDark() {
return Theme.of(context).brightness == Brightness.dark;
}
bool _isLabel() {
for (final CusStep step in widget.steps) {
if (step.label != null) {
return true;
}
}
return false;
}
Widget _buildLine(bool visible) {
return Container(
width: visible ? 1.0 : 0.0,
height: 16.0,
color: widget.lineColor,
);
}
Widget _buildCircleChild(int index, bool oldState) {
final CusStepState state =
oldState ? _oldStates[index]! : widget.steps[index].state;
final bool isDarkActive = _isDark() && widget.steps[index].isActive;
switch (state) {
case CusStepState.indexed:
case CusStepState.disabled:
return Text(
'${index + 1}',
style: isDarkActive
? _kStepStyle.copyWith(color: Colors.black87)
: _kStepStyle,
);
case CusStepState.editing:
return Icon(
Icons.edit,
color: isDarkActive ? _kCircleActiveDark : _kCircleActiveLight,
size: 18.0,
);
case CusStepState.complete:
return Icon(
Icons.check,
color: isDarkActive ? _kCircleActiveDark : _kCircleActiveLight,
size: 18.0,
);
case CusStepState.error:
return const Text('!', style: _kStepStyle);
}
}
Color _circleColor(int index) {
final ColorScheme colorScheme = Theme.of(context).colorScheme;
if (!_isDark()) {
return widget.steps[index].isActive
? colorScheme.primary
: colorScheme.onSurface.withOpacity(0.38);
} else {
return widget.steps[index].isActive
? colorScheme.secondary
: colorScheme.background;
}
}
Widget _buildCircle(int index, bool oldState) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 8.0),
width: _kStepSize,
height: _kStepSize,
child: AnimatedContainer(
curve: Curves.fastOutSlowIn,
duration: kThemeAnimationDuration,
decoration: BoxDecoration(
color: _circleColor(index),
shape: BoxShape.circle,
),
child: Center(
child: _buildCircleChild(
index, oldState && widget.steps[index].state == CusStepState.error),
),
),
);
}
Widget _buildTriangle(int index, bool oldState) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 8.0),
width: _kStepSize,
height: _kStepSize,
child: Center(
child: SizedBox(
width: _kStepSize,
height:
_kTriangleHeight, // Height of 24dp-long-sided equilateral triangle.
child: CustomPaint(
painter: _TrianglePainter(
color: _isDark() ? _kErrorDark : _kErrorLight,
),
child: Align(
alignment: const Alignment(
0.0, 0.8), // 0.8 looks better than the geometrical 0.33.
child: _buildCircleChild(index,
oldState && widget.steps[index].state != CusStepState.error),
),
),
),
),
);
}
Widget _buildIcon(int index) {
if (widget.steps[index].state != _oldStates[index]) {
return AnimatedCrossFade(
firstChild: _buildCircle(index, true),
secondChild: _buildTriangle(index, true),
firstCurve: const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn),
secondCurve: const Interval(0.4, 1.0, curve: Curves.fastOutSlowIn),
sizeCurve: Curves.fastOutSlowIn,
crossFadeState: widget.steps[index].state == CusStepState.error
? CrossFadeState.showSecond
: CrossFadeState.showFirst,
duration: kThemeAnimationDuration,
);
} else {
if (widget.steps[index].state != CusStepState.error) {
return _buildCircle(index, false);
} else {
return _buildTriangle(index, false);
}
}
}
Widget _buildVerticalControls(int stepIndex) {
if (widget.controlsBuilder != null) {
return widget.controlsBuilder!(
context,
ControlsDetails(
currentStep: widget.currentStep,
onStepContinue: widget.onStepContinue,
onStepCancel: widget.onStepCancel,
stepIndex: stepIndex,
),
);
}
final Color cancelColor;
switch (Theme.of(context).brightness) {
case Brightness.light:
cancelColor = Colors.black54;
break;
case Brightness.dark:
cancelColor = Colors.white70;
break;
}
final ThemeData themeData = Theme.of(context);
final ColorScheme colorScheme = themeData.colorScheme;
final MaterialLocalizations localizations =
MaterialLocalizations.of(context);
const OutlinedBorder buttonShape = RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2)));
const EdgeInsets buttonPadding = EdgeInsets.symmetric(horizontal: 16.0);
return Container(
margin: const EdgeInsets.only(top: 16.0),
child: ConstrainedBox(
constraints: const BoxConstraints.tightFor(height: 48.0),
child: Row(
children: <Widget>[
TextButton(
onPressed: widget.onStepContinue,
style: ButtonStyle(
foregroundColor: MaterialStateProperty.resolveWith<Color?>(
(Set<MaterialState> states) {
return states.contains(MaterialState.disabled)
? null
: (_isDark()
? colorScheme.onSurface
: colorScheme.onPrimary);
}),
backgroundColor: MaterialStateProperty.resolveWith<Color?>(
(Set<MaterialState> states) {
return _isDark() || states.contains(MaterialState.disabled)
? null
: colorScheme.primary;
}),
padding: const MaterialStatePropertyAll<EdgeInsetsGeometry>(
buttonPadding),
shape:
const MaterialStatePropertyAll<OutlinedBorder>(buttonShape),
),
child: Text(themeData.useMaterial3
? localizations.continueButtonLabel
: localizations.continueButtonLabel.toUpperCase()),
),
Container(
margin: const EdgeInsetsDirectional.only(start: 8.0),
child: TextButton(
onPressed: widget.onStepCancel,
style: TextButton.styleFrom(
foregroundColor: cancelColor,
padding: buttonPadding,
shape: buttonShape,
),
child: Text(themeData.useMaterial3
? localizations.cancelButtonLabel
: localizations.cancelButtonLabel.toUpperCase()),
),
),
],
),
),
);
}
TextStyle _titleStyle(int index) {
final ThemeData themeData = Theme.of(context);
final TextTheme textTheme = themeData.textTheme;
switch (widget.steps[index].state) {
case CusStepState.indexed:
case CusStepState.editing:
case CusStepState.complete:
return textTheme.bodyLarge!;
case CusStepState.disabled:
return textTheme.bodyLarge!.copyWith(
color: _isDark() ? _kDisabledDark : _kDisabledLight,
);
case CusStepState.error:
return textTheme.bodyLarge!.copyWith(
color: _isDark() ? _kErrorDark : _kErrorLight,
);
}
}
TextStyle _subtitleStyle(int index) {
final ThemeData themeData = Theme.of(context);
final TextTheme textTheme = themeData.textTheme;
switch (widget.steps[index].state) {
case CusStepState.indexed:
case CusStepState.editing:
case CusStepState.complete:
return textTheme.bodySmall!;
case CusStepState.disabled:
return textTheme.bodySmall!.copyWith(
color: _isDark() ? _kDisabledDark : _kDisabledLight,
);
case CusStepState.error:
return textTheme.bodySmall!.copyWith(
color: _isDark() ? _kErrorDark : _kErrorLight,
);
}
}
TextStyle _labelStyle(int index) {
final ThemeData themeData = Theme.of(context);
final TextTheme textTheme = themeData.textTheme;
switch (widget.steps[index].state) {
case CusStepState.indexed:
case CusStepState.editing:
case CusStepState.complete:
return textTheme.bodyLarge!;
case CusStepState.disabled:
return textTheme.bodyLarge!.copyWith(
color: _isDark() ? _kDisabledDark : _kDisabledLight,
);
case CusStepState.error:
return textTheme.bodyLarge!.copyWith(
color: _isDark() ? _kErrorDark : _kErrorLight,
);
}
}
Widget _buildHeaderText(int index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
AnimatedDefaultTextStyle(
style: _titleStyle(index),
duration: kThemeAnimationDuration,
curve: Curves.fastOutSlowIn,
child: widget.steps[index].title,
),
if (widget.steps[index].subtitle != null)
Container(
margin: const EdgeInsets.only(top: 2.0),
child: AnimatedDefaultTextStyle(
style: _subtitleStyle(index),
duration: kThemeAnimationDuration,
curve: Curves.fastOutSlowIn,
child: widget.steps[index].subtitle!,
),
),
],
);
}
Widget _buildLabelText(int index) {
if (widget.steps[index].label != null) {
return AnimatedDefaultTextStyle(
style: _labelStyle(index),
duration: kThemeAnimationDuration,
child: widget.steps[index].label!,
);
}
return const SizedBox.shrink();
}
Widget _buildVerticalHeader(int index) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 24.0),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
_buildLine(!_isFirst(index)),
_buildIcon(index),
_buildLine(!_isLast(index)),
],
),
Expanded(
child: Container(
margin: const EdgeInsetsDirectional.only(start: 12.0),
child: _buildHeaderText(index),
),
),
],
),
);
}
Widget _buildVerticalBody(int index) {
return Stack(
children: <Widget>[
PositionedDirectional(
start: 24.0,
top: 0.0,
bottom: 0.0,
child: SizedBox(
width: 24.0,
child: Center(
child: SizedBox(
width: _isLast(index) ? 0.0 : 1.0,
child: Container(
color: widget.lineColor,
),
),
),
),
),
AnimatedCrossFade(
firstChild: Container(height: 0.0),
secondChild: Container(
margin: widget.margin ??
const EdgeInsetsDirectional.only(
start: 60.0,
end: 24.0,
bottom: 24.0,
),
child: Column(
children: <Widget>[
widget.steps[index].content,
_buildVerticalControls(index),
],
),
),
firstCurve: const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn),
secondCurve: const Interval(0.4, 1.0, curve: Curves.fastOutSlowIn),
sizeCurve: Curves.fastOutSlowIn,
crossFadeState: _isCurrent(index)
? CrossFadeState.showSecond
: CrossFadeState.showFirst,
duration: kThemeAnimationDuration,
),
],
);
}
Widget _buildVertical() {
return ListView(
shrinkWrap: true,
physics: widget.physics,
children: <Widget>[
for (int i = 0; i < widget.steps.length; i += 1)
Column(
key: _keys[i],
children: <Widget>[
InkWell(
onTap: widget.steps[i].state != CusStepState.disabled
? () {
// In the vertical case we need to scroll to the newly tapped
// step.
Scrollable.ensureVisible(
_keys[i].currentContext!,
curve: Curves.fastOutSlowIn,
duration: kThemeAnimationDuration,
);
widget.onStepTapped?.call(i);
}
: null,
canRequestFocus: widget.steps[i].state != CusStepState.disabled,
child: _buildVerticalHeader(i),
),
_buildVerticalBody(i),
],
),
],
);
}
Widget _buildHorizontal() {
final List<Widget> children = <Widget>[
for (int i = 0; i < widget.steps.length; i += 1) ...<Widget>[
InkResponse(
onTap: widget.steps[i].state != CusStepState.disabled
? () {
widget.onStepTapped?.call(i);
}
: null,
canRequestFocus: widget.steps[i].state != CusStepState.disabled,
child: Row(
children: <Widget>[
SizedBox(
height: _isLabel() ? 104.0 : 72.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (widget.steps[i].label != null)
const SizedBox(
height: 24.0,
),
Center(child: _buildIcon(i)),
if (widget.steps[i].label != null)
SizedBox(
height: 24.0,
child: _buildLabelText(i),
),
],
),
),
Container(
margin: const EdgeInsetsDirectional.only(start: 12.0),
child: _buildHeaderText(i),
),
],
),
),
if (!_isLast(i))
Expanded(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
height: 1.0,
color: widget.lineColor,
),
),
],
];
final List<Widget> stepPanels = <Widget>[];
for (int i = 0; i < widget.steps.length; i += 1) {
stepPanels.add(
Visibility(
maintainState: true,
visible: i == widget.currentStep,
child: widget.steps[i].content,
),
);
}
return Column(
children: <Widget>[
Material(
elevation: widget.elevation ?? 2,
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 24.0),
child: Row(
children: children,
),
),
),
Expanded(
child: ListView(
physics: widget.physics,
padding: const EdgeInsets.all(24.0),
children: <Widget>[
AnimatedSize(
curve: Curves.fastOutSlowIn,
duration: kThemeAnimationDuration,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: stepPanels),
),
_buildVerticalControls(widget.currentStep),
],
),
),
],
);
}
#override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMaterialLocalizations(context));
assert(() {
if (context.findAncestorWidgetOfExactType<Stepper>() != null) {
throw FlutterError(
'Steppers must not be nested.\n'
'The material specification advises that one should avoid embedding '
'steppers within steppers. '
'https://material.io/archive/guidelines/components/steppers.html#steppers-usage',
);
}
return true;
}());
switch (widget.type) {
case CusStepperType.vertical:
return _buildVertical();
case CusStepperType.horizontal:
return _buildHorizontal();
}
}
}
class _TrianglePainter extends CustomPainter {
_TrianglePainter({
required this.color,
});
final Color color;
#override
bool hitTest(Offset point) => true;
#override
bool shouldRepaint(_TrianglePainter oldPainter) {
return oldPainter.color != color;
}
#override
void paint(Canvas canvas, Size size) {
final double base = size.width;
final double halfBase = size.width / 2.0;
final double height = size.height;
final List<Offset> points = <Offset>[
Offset(0.0, height),
Offset(base, height),
Offset(halfBase, 0.0),
];
canvas.drawPath(
Path()..addPolygon(points, true),
Paint()..color = color,
);
}
}
EXAMPLE CODEBASE ON HOW TO USE THE CusStepper
import 'package:flutter/material.dart';
import 'package:tester/stepper/stepper.dart'; // this is the file path of where you store your `cusStepper`
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Tester',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _index = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: CusStepper(
lineColor: Colors.red, // new line add for the color change
currentStep: _index,
onStepCancel: () {
if (_index > 0) {
setState(() {
_index -= 1;
});
}
},
onStepContinue: () {
if (_index <= 0) {
setState(() {
_index += 1;
});
}
},
onStepTapped: (int index) {
setState(() {
_index = index;
});
},
steps: <CusStep>[
CusStep(
title: const Text('Step 1 title'),
content: Container(
alignment: Alignment.centerLeft,
child: const Text('Content for Step 1')),
),
const CusStep(
title: Text('Step 2 title'),
content: Text('Content for Step 2'),
),
],
),
),
);
}
}
OUTPUT
Leave a comment below if you have any questions or help on this.
Bye!
The Flutter Default stepper Line has Static color use so can't change this.
chack below Image.
Here https://fluttergems.dev/stepper mention many stepper package used as you want.
I have found a code that i tried to replicate. It has an app itself and it is working. However when i tried to write the code down it shows the problem Unsupported operation NaN or infinity toInt for a function named TweenAnimation. I am still trying to learn how to use tween animation so any pros have any idea on this problem?
below is the code for the animation
import 'package:flutter/material.dart';
class AnimatedFlipCounter extends StatelessWidget {
final int value;
final Duration duration;
final double size;
final Color color;
const AnimatedFlipCounter({
Key? key,
required this.value,
required this.duration,
this.size = 72,
this.color = Colors.black,
}) : super(key: key);
#override
Widget build(BuildContext context) {
List<int> digits = value == 0 ? [0] : [];
int v = value;
if (v < 0) {
v *= -1;
}
while (v > 0) {
digits.add(v);
v = v ~/ 10;
}
return Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(digits.length, (int i) {
return _SingleDigitFlipCounter(
key: ValueKey(digits.length - i),
value: digits[digits.length - i - 1].toDouble(),
duration: duration,
height: size,
width: size / 1.8,
color: color,
);
}),
);
}
}
class _SingleDigitFlipCounter extends StatelessWidget {
final double value;
final Duration duration;
final double height;
final double width;
final Color color;
const _SingleDigitFlipCounter({
required Key key,
required this.value,
required this.duration,
required this.height,
required this.width,
required this.color,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return TweenAnimationBuilder(
tween: Tween(begin: value, end: value),
duration: duration,
builder: (context,double value, child) {
final whole = 1.0 ~/ value ;
final decimal = value - whole;
return SizedBox(
height: height,
width: width,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
_buildSingleDigit(
digit: whole % 10,
offset: height * decimal,
opacity: 1.0 - decimal,
),
_buildSingleDigit(
digit: (whole + 1) % 10,
offset: height * decimal - height,
opacity: decimal,
),
],
),
);
},
);
}
Widget _buildSingleDigit({required int digit, required double offset, required double opacity}) {
return Positioned(
child: SizedBox(
width: width,
child: Opacity(
opacity: opacity,
child: Text(
"$digit",
style: TextStyle(
fontSize: height, color: color, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
),
),
bottom: offset,
);
}
below is the main page that calls this widget
import 'package:carousel_slider/carousel_slider.dart';
import 'package:daily5/helpers/constants_tasbih.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:get_storage/get_storage.dart';
import 'package:vibration/vibration.dart';
import '../../helpers/animated_flip_counter.dart';
class Tasbih extends StatefulWidget {
const Tasbih({Key? key}) : super(key: key);
#override
_TasbihState createState() => _TasbihState();
}
class _TasbihState extends State<Tasbih> {
final PageController _controller =
PageController(viewportFraction: 0.1, initialPage: 5);
final int _numberOfCountsToCompleteRound = 33;
int _imageIndex = 1;
int _beadCounter = 0;
int _roundCounter = 0;
int _accumulatedCounter = 0;
bool _canVibrate = true;
bool _isDisposed = false;
final List<Color> _bgColour = [
Colors.teal.shade50,
Colors.lime.shade50,
Colors.lightBlue.shade50,
Colors.pink.shade50,
Colors.black12
];
final CarouselController _buttonCarouselController = CarouselController();
#override
void initState() {
super.initState();
_loadSettings();
}
#override
void dispose() {
_isDisposed = true;
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: _clicked,
onVerticalDragStart: (_) => _clicked(),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Expanded(
flex: 2,
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: [
const SizedBox(width: 45),
IconButton(
tooltip: 'Change colour',
icon: const Icon(Icons.palette),
onPressed: () {
setState(() {
_imageIndex < 5
? _imageIndex++
: _imageIndex = 1;
});
}),
IconButton(
tooltip: 'Reset counter',
icon: const Icon(Icons.refresh),
onPressed: () {
confirmReset(context, _resetEverything);
}),
],
),
const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
textDirection: TextDirection.ltr,
children: <Widget>[
_Counter(
counter: _roundCounter, counterName: 'Round'),
_Counter(counter: _beadCounter, counterName: 'Beads'),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 32),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text('Accumulated'),
const SizedBox(width: 10),
AnimatedFlipCounter(
value: _accumulatedCounter,
duration: const Duration(milliseconds: 730),
size: 14),
],
),
),
CarouselSlider(
carouselController: _buttonCarouselController,
options: CarouselOptions(
height: 100.0,
enlargeCenterPage: true,
),
items: [1, 2, 3, 4].map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(
horizontal: 5.0),
decoration: BoxDecoration(
color: _bgColour[_imageIndex - 1],
borderRadius: BorderRadius.circular(12)),
child: Image.asset('assets/images/zikr/$i.png'));
},
);
}).toList(),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
onPressed: () {
_buttonCarouselController.previousPage();
},
icon: const Icon(Icons.navigate_before)),
IconButton(
onPressed: () {
_buttonCarouselController.nextPage();
},
icon: const Icon(Icons.navigate_next)),
],
),
const Spacer()
],
),
)),
Expanded(
flex: 1,
child: PageView.builder(
reverse: true,
physics: const NeverScrollableScrollPhysics(),
controller: _controller,
scrollDirection: Axis.vertical,
itemBuilder: (_, __) {
return Image.asset(
'assets/images/beads/bead-$_imageIndex.png',
);
},
itemCount: null,
),
),
],
),
),
);
}
void _loadSettings() async {
bool? canVibrate = await Vibration.hasVibrator();
if (!_isDisposed) {
setState(() {
_canVibrate = canVibrate!;
_loadData();
});
}
}
void _loadData() {
if (!_isDisposed) {
setState(() {
_beadCounter = GetStorage().read(kBeadsCount) ?? 0;
_roundCounter = GetStorage().read(kRoundCount) ?? 0;
_accumulatedCounter =
_roundCounter * _numberOfCountsToCompleteRound + _beadCounter;
});
}
}
void _resetEverything() {
GetStorage().write(kBeadsCount, 0);
GetStorage().write(kRoundCount, 0);
_loadData();
}
void _clicked() {
if (!_isDisposed) {
setState(() {
_beadCounter++;
_accumulatedCounter++;
if (_beadCounter > _numberOfCountsToCompleteRound) {
_beadCounter = 1;
_roundCounter++;
if (_canVibrate) Vibration.vibrate(duration: 100, amplitude: 100);
}
});
}
GetStorage().write(kBeadsCount, _beadCounter);
GetStorage().write(kRoundCount, _roundCounter);
int nextPage = _controller.page!.round() + 1;
_controller.animateToPage(nextPage,
duration: const Duration(milliseconds: 200), curve: Curves.easeIn);
}
}
class _Counter extends StatelessWidget {
const _Counter(
{Key? key,
required this.counter,
this.tsCounter =
const TextStyle(fontSize: 50, fontWeight: FontWeight.bold),
required this.counterName,
this.tsCounterName = const TextStyle(
fontSize: 20,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w300)})
: super(key: key);
final int counter;
final TextStyle tsCounter;
final String counterName;
final TextStyle tsCounterName;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
AnimatedFlipCounter(
duration: const Duration(milliseconds: 300),
value: counter,
),
Text(counterName, style: tsCounterName)
],
);
}
}
void confirmReset(BuildContext context, VoidCallback callback) {
const _confirmText = Text('Confirm', style: TextStyle(color: Colors.red));
const _cancelText = Text('Cancel');
const _dialogTitle = Text("Reset Counter?");
const _dialogContent = Text("This action can't be undone");
void _confirmResetAction() {
callback();
showSnackBar(
context: context,
label: 'Cleared',
icon: CupertinoIcons.check_mark_circled);
Navigator.of(context).pop();
}
showDialog(
context: context,
builder: (_) {
return kIsWeb
? AlertDialog(
title: _dialogTitle,
content: _dialogContent,
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: _cancelText,
),
TextButton(
onPressed: _confirmResetAction,
child: _confirmText,
),
],
)
: CupertinoAlertDialog(
title: _dialogTitle,
content: _dialogContent,
actions: [
CupertinoDialogAction(
child: _cancelText,
onPressed: () => Navigator.of(context).pop(),
),
CupertinoDialogAction(
child: _confirmText,
onPressed: _confirmResetAction,
),
],
);
},
);
}
void showSnackBar({required BuildContext context, required String label, required IconData icon}) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: [
Icon(
icon,
color: Colors.white60,
),
const SizedBox(width: 5),
Text(label)
],
),
),
);
}
Supposedly it should beads that can be counted down but the animation cant load.
I'm using animated containers and I want them to change color when I use NotifyListeners();
Thing is, the color only updates when you click on the container, otherwise it remains the same.
I tried switching to normal containers, I tried changing the home screen to stateful, nothing seems to work, any help is appreciated.
Github link: https://github.com/amrogad/beat_maker
void main() {
runApp(MultiProvider(providers: [
ChangeNotifierProvider<SoundProvider>(create: (_) => SoundProvider(),
),
ChangeNotifierProvider<IconProvider>(create: (_) => IconProvider(),
),
], child: MyApp()));
}
class IconProvider extends ChangeNotifier {
Color primary1 = Colors.deepPurpleAccent;
Color secondary1 = Colors.deepPurple;
Color primary2 = Colors.yellowAccent;
Color secondary2 = Colors.orangeAccent;
Color primary3 = Colors.cyanAccent;
Color secondary3 = Colors.blue;
Color primary4 = Colors.lime;
Color secondary4 = Colors.lightGreen;
void changeIcons(
Color primaryPath1,
Color secondaryPath1,
Color primaryPath2,
Color secondaryPath2,
Color primaryPath3,
Color secondaryPath3,
Color primaryPath4,
Color secondaryPath4,
) {
primary1 = primaryPath1;
secondary1 = secondaryPath1;
primary2 = primaryPath2;
secondary2 = secondaryPath2;
primary3 = primaryPath3;
secondary3 = secondaryPath3;
primary4 = primaryPath4;
secondary4 = secondaryPath4;
notifyListeners();
}
}
class HomeScreen extends StatelessWidget {
static const String routeName = 'Home Screen';
#override
Widget build(BuildContext context) {
var soundProvider = Provider.of<SoundProvider>(context);
return SafeArea(
child: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.black,
title: Text(
'Beat Maker',
style: GoogleFonts.anton(color: Colors.lime, fontSize: 24),
),
centerTitle: true,
actions: [
GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(SettingsScreen.routeName);
},
child: Icon(
Icons.settings,
color: Colors.lime,
size: 24,
)),
SizedBox(
width: 10,
),
],
),
body: Consumer<IconProvider>(
builder: (context, icon, child) {
return GridView(
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4
),
children: [
SoundButton(
icon.primary1, icon.secondary1, soundProvider.note1),
SoundButton(
icon.primary2, icon.secondary2, soundProvider.note2),
SoundButton(
icon.primary3, icon.secondary3, soundProvider.note3),
SoundButton(
icon.primary4, icon.secondary4, soundProvider.note4)
],
);
}
),
),
);
}
class SoundButton extends StatefulWidget {
final Color mainColor;
final Color sideColor;
final note;
const SoundButton(this.mainColor, this.sideColor, this.note, {Key? key})
: super(key: key);
#override
State<SoundButton> createState() => _SoundButtonState();
}
class _SoundButtonState extends State<SoundButton> {
late Color _mainColor;
late Color _sideColor;
final player = AudioPlayer();
#override
void initState() {
_mainColor = widget.mainColor;
_sideColor = widget.sideColor;
super.initState();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
_mainColor = Colors.redAccent;
_sideColor = Colors.red;
player.play(AssetSource(widget.note));
});
},
child: AnimatedContainer(
decoration: BoxDecoration(
border: Border.all(width: 1),
gradient: RadialGradient(colors: [_mainColor, _sideColor]),
boxShadow: const [
BoxShadow(
blurRadius: 1.0,
spreadRadius: 1.0,
offset: Offset(
1.0,
1.0,
),
),
]),
curve: Curves.easeOutCubic,
onEnd: () => Reset(),
duration: const Duration(milliseconds: 100),
),
);
}
void Reset() {
setState(() {
_mainColor = widget.mainColor;
_sideColor = widget.sideColor;
});
}
}
class OpenIconBottomSheet extends StatelessWidget {
const OpenIconBottomSheet({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var iconProvider = Provider.of<IconProvider>(context);
return Container(
height: 106,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12), topRight: Radius.circular(12)),
color: Colors.black,
border: Border.all(width: 3, color: Colors.lime)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
InkWell(
onTap: () {
iconProvider.changeIcons(
Colors.deepPurpleAccent,
Colors.deepPurple,
Colors.yellowAccent,
Colors.orangeAccent,
Colors.cyanAccent,
Colors.blue,
Colors.lime,
Colors.lightGreen,
);
Navigator.pop(context);
},
child: isSelected(
'Main Theme',
iconProvider.primary1 == Colors.deepPurpleAccent
? true
: false)),
Container(
width: double.infinity,
height: 3,
color: Colors.lime,
),
SizedBox(
height: 7,
),
InkWell(
onTap: () {
iconProvider.changeIcons(
Colors.white70,
Colors.white,
Colors.pinkAccent,
Colors.pink,
Colors.tealAccent,
Colors.teal,
Colors.amberAccent,
Colors.redAccent,
);
Navigator.pop(context);
},
child: isSelected('Secondary Theme',
iconProvider.primary1 == Colors.white70 ? true : false)),
],
),
);
}
Widget isSelected(String text, bool selected) {
if (selected) {
return Padding(
padding: EdgeInsets.all(6),
child: Text(text,
style: GoogleFonts.anton(color: Colors.lime, fontSize: 24)),
);
} else {
return Padding(
padding: EdgeInsets.all(6),
child: Text(text,
style: GoogleFonts.anton(color: Colors.white, fontSize: 24)),
);
}
}
}
I am building a quiz app and I created a custom widget to save me a lot of time as I have a lot of questions for the quiz. Everything works apart from the scoring system. If I create multiple instances of the same widget the score will not be incremented and it will stay on 1. Is there any way I can pass each score of the widgets to a global variable in my main widget so then I can add all the scores? (I'm new to flutter).
Custom Widget
class Questions extends StatefulWidget {
final String imagePath;
final String question;
final String answer1;
final String answer2;
final String answer3;
final String answer4;
final bool iscorrectAnswer1;
final bool iscorrectAnswer2;
final bool iscorrectAnswer3;
final bool iscorrectAnswer4;
int score = 0;
bool questionsAnswered = false;
Questions(
this.imagePath,
this.question,
this.answer1,
this.answer2,
this.answer3,
this.answer4,
this.iscorrectAnswer1,
this.iscorrectAnswer2,
this.iscorrectAnswer3,
this.iscorrectAnswer4,
);
#override
_QuestionsState createState() => _QuestionsState();
}
class _QuestionsState extends State<Questions> {
disableButton() {
setState(() {
widget.questionsAnswered = true;
Quiz().score += widget.score;
});
}
#override
#override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(
width: 600,
height: 600,
child: Image.asset(widget.imagePath),
),
Align(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.only(
top: 20,
),
child: Text(
widget.question,
style: TextStyle(
color: Colors.white,
fontSize: 38,
),
),
)),
Padding(
padding: EdgeInsets.only(
top: 40,
),
child: SizedBox(
width: 500,
height: 60,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color(0xFF304e60),
),
),
child: Text(
widget.answer1,
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
onPressed: widget.questionsAnswered == false
? () {
setState(() {
if (widget.iscorrectAnswer1 == true) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Correct!'),
),
);
disableButton();
widget.score += 1;
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text('Wrong Answer!'),
));
}
});
print(widget.iscorrectAnswer1);
print(widget.score);
}
: null),
),
)),
Padding(
padding: EdgeInsets.only(
top: 10,
),
child: SizedBox(
width: 500,
height: 60,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Color(0xFF565462))),
child: Text(
widget.answer2,
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
onPressed: widget.questionsAnswered == false
? () {
setState(() {
if (widget.iscorrectAnswer2 == true) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Correct!'),
),
);
widget.score += 1;
} else {
disableButton();
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text('Wrong Answer!'),
));
}
});
}
: null),
),
)),
Padding(
padding: EdgeInsets.only(
top: 10,
),
child: SizedBox(
width: 500,
height: 60,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Color(0xFF84693b))),
child: Text(
widget.answer3,
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
onPressed: widget.questionsAnswered == false
? () {
setState(() {
if (widget.iscorrectAnswer3 == true) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Correct!'),
),
);
widget.score += 1;
} else {
disableButton();
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text('Wrong Answer!'),
));
}
});
}
: null),
),
),
)
],
);
}
}
Main widget where I call this custom widget
class Quiz extends StatefulWidget {
Quiz({Key? key}) : super(key: key);
int score = 0;
#override
_QuizState createState() => _QuizState();
}
class _QuizState extends State<Quiz> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('CyberQuiz'),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Questions(
'images/malware_quiz.jpeg',
'1. What is a malware?',
'Designed to damage computers, servers or any other devices',
"Used to get user's credentials",
"It's used to destroy networks",
'',
true,
false,
false,
false,
),
],
)));
}
}
As you suggest in your question, you could create a global variable and increment/decrease/reset that.
Basic example code:
import 'package:flutter/material.dart';
class Score {
static int score = 0;
}
class ScoreCounter extends StatefulWidget {
const ScoreCounter({Key? key}) : super(key: key);
#override
State<ScoreCounter> createState() => _ScoreCounterState();
}
class _ScoreCounterState extends State<ScoreCounter> {
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
setState(() {
Score.score++;
});
},
child: Text('increase score'),
),
),
Expanded(child: Text(Score.score.toString()))
],
);
}
}
Another option is to use the Provider package - link here which has an example
Provider Package
I can disable the tooltip statically.
But I want to disable tooltip dynamically when i click flatbutton.But Couldnt disable dynamically and i have no idea to do that.
This is my code:
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(home: HelloWorld(),debugShowCheckedModeBanner: false,));
}
class HelloWorld extends StatefulWidget {
#override
_HelloWorldState createState() => _HelloWorldState();
}
class _HelloWorldState extends State<HelloWorld> {
bool check = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: <Widget>[
TopToolbar(),
FlatButton(
child: Text("Disable Tooltip"),
onPressed: () {
setState(() {
TopToolbar toolbar = new TopToolbar();
toolbar.showTooltip = false;
});
},
),
]),
),
));
}
}
class TopToolbar extends StatefulWidget {
bool showTooltip;
final Color backgroundColor;
final double height;
bool isVisible;
TopToolbar({
this.height = 55,
this.isVisible = true,
this.backgroundColor = const Color(0xFFEEEEEE),
Key key,this.showTooltip=true,
}) : super(key: key);
#override
_TopToolbarState createState() => _TopToolbarState();
}
class _TopToolbarState extends State<TopToolbar> {
#override
Widget build(BuildContext context) {
if (widget.isVisible) {
return Container(
foregroundDecoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey,
),
),
),
margin: EdgeInsets.only(bottom: 1),
color: widget.backgroundColor,
height: widget.height,
child: Stack(
children: <Widget>[
Positioned(
top: 7,
right: 60,
height: 40,
width: 40,
child: RawMaterialButton(
elevation: 0.0,
fillColor: widget.backgroundColor,
splashColor: Colors.grey[300],
child: IconButton(
icon: Icon(
Icons.bookmark,
color: Colors.grey[500],
size: 25,
),
onPressed: (){},
tooltip: widget.showTooltip ? "Bookmark" : null,
),
onPressed: (){},
),
),
],
),
);
} else {
return Container();
}
}
}
If I give statically false. it works fine.
For example : If add child like TopToolbar(showTooltip : false),it works fine,
But If i give toolbar.showTooltip = false in Flatbutton onPressed method,it doesnt work.
I want to disble it in dynamically. please help me to do that.
we can hide or deactivate tooltip programmatically like below,
Future.delayed(
Duration(seconds: 2),
() {
tooltip?.deactivate();
}
);
Here, we can set time according to your requirement.(Currently, we are set 2 sec.)
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: HelloWorld(),
debugShowCheckedModeBanner: false,
));
}
class HelloWorld extends StatefulWidget {
#override
_HelloWorldState createState() => _HelloWorldState();
}
class _HelloWorldState extends State<HelloWorld> {
bool check = false;
bool showTooltip = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: <Widget>[
TopToolbar(showTooltip: showTooltip),
FlatButton(
child: Text("Disable Tooltip"),
onPressed: () {
setState(() {
showTooltip = false;
});
},
),
]),
),
));
}
}
class TopToolbar extends StatefulWidget {
final bool showTooltip;
final Color backgroundColor;
final double height;
final bool isVisible;
TopToolbar({
this.height = 55,
this.isVisible = true,
this.backgroundColor = const Color(0xFFEEEEEE),
Key key,
this.showTooltip = true,
}) : super(key: key);
#override
_TopToolbarState createState() => _TopToolbarState();
}
class _TopToolbarState extends State<TopToolbar> {
#override
Widget build(BuildContext context) {
if (widget.isVisible) {
return Container(
foregroundDecoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey,
),
),
),
margin: EdgeInsets.only(bottom: 1),
color: widget.backgroundColor,
height: widget.height,
child: Stack(
children: <Widget>[
Positioned(
top: 7,
right: 60,
height: 40,
width: 40,
child: RawMaterialButton(
elevation: 0.0,
fillColor: widget.backgroundColor,
splashColor: Colors.grey[300],
child: IconButton(
icon: Icon(
Icons.bookmark,
color: Colors.grey[500],
size: 25,
),
onPressed: () {},
tooltip: widget.showTooltip ? 'Bookmark' : null,
),
onPressed: () {},
),
),
],
),
);
} else {
return Container();
}
}
}
I've used this method to hide tooltips:
Tooltip(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0),
),
textStyle: TextStyle(color: Colors.white.withOpacity(0)),
message: 'Certificates',
child: Container()
);
Make the property message='' // empty string
setState((){messageText=''});
Tooltip(
message: messageText,
...
)