mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Adds support for StepStyle visual property bundle to the Step widget (#140825)
Fixes #140770 and #103124 Adds the possibility of passing a height and width to icons. And also a margin for the distance of the lines between the icons.
This commit is contained in:
parent
ec97b6d085
commit
f40a99ce7e
105
examples/api/lib/material/stepper/step_style.0.dart
Normal file
105
examples/api/lib/material/stepper/step_style.0.dart
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Flutter code sample for [StepStyle].
|
||||
|
||||
void main() => runApp(const StepStyleExampleApp());
|
||||
|
||||
class StepStyleExampleApp extends StatelessWidget {
|
||||
|
||||
const StepStyleExampleApp({ super.key });
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
appBar: AppBar(title: const Text('Step Style Example')),
|
||||
body: const Center(
|
||||
child: StepStyleExample(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StepStyleExample extends StatefulWidget {
|
||||
const StepStyleExample({ super.key });
|
||||
|
||||
@override
|
||||
State<StepStyleExample> createState() => _StepStyleExampleState();
|
||||
}
|
||||
|
||||
class _StepStyleExampleState extends State<StepStyleExample> {
|
||||
final StepStyle _stepStyle = StepStyle(
|
||||
connectorThickness: 10,
|
||||
color: Colors.white,
|
||||
connectorColor: Colors.red,
|
||||
indexStyle: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 20,
|
||||
),
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stepper(
|
||||
type: StepperType.horizontal,
|
||||
stepIconHeight: 48,
|
||||
stepIconWidth: 48,
|
||||
stepIconMargin: EdgeInsets.zero,
|
||||
steps: <Step>[
|
||||
Step(
|
||||
title: const SizedBox.shrink(),
|
||||
content: const SizedBox.shrink(),
|
||||
isActive: true,
|
||||
stepStyle: _stepStyle,
|
||||
),
|
||||
Step(
|
||||
title: const SizedBox.shrink(),
|
||||
content: const SizedBox.shrink(),
|
||||
isActive: true,
|
||||
stepStyle: _stepStyle.copyWith(
|
||||
connectorColor: Colors.orange,
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[
|
||||
Colors.white,
|
||||
Colors.black,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Step(
|
||||
title: const SizedBox.shrink(),
|
||||
content: const SizedBox.shrink(),
|
||||
isActive: true,
|
||||
stepStyle: _stepStyle.copyWith(
|
||||
connectorColor: Colors.blue,
|
||||
),
|
||||
),
|
||||
Step(
|
||||
title: const SizedBox.shrink(),
|
||||
content: const SizedBox.shrink(),
|
||||
isActive: true,
|
||||
stepStyle: _stepStyle.merge(
|
||||
StepStyle(
|
||||
color: Colors.white,
|
||||
indexStyle: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 20,
|
||||
),
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
72
examples/api/test/material/stepper/step_style.0_test.dart
Normal file
72
examples/api/test/material/stepper/step_style.0_test.dart
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_api_samples/material/stepper/step_style.0.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('StepStyle Smoke Test', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.StepStyleExampleApp(),
|
||||
);
|
||||
|
||||
expect(find.widgetWithText(AppBar, 'Step Style Example'), findsOneWidget);
|
||||
|
||||
final Stepper stepper = tester.widget<Stepper>(find.byType(Stepper));
|
||||
// Check that the stepper has the correct properties.
|
||||
expect(stepper.type, StepperType.horizontal);
|
||||
expect(stepper.stepIconHeight, 48);
|
||||
expect(stepper.stepIconWidth, 48);
|
||||
expect(stepper.stepIconMargin, EdgeInsets.zero);
|
||||
|
||||
// Check that the first step has the correct properties.
|
||||
final Step firstStep = stepper.steps[0];
|
||||
expect(firstStep.title, isA<SizedBox>());
|
||||
expect(firstStep.content, isA<SizedBox>());
|
||||
expect(firstStep.isActive, true);
|
||||
expect(firstStep.stepStyle?.connectorThickness, 10);
|
||||
expect(firstStep.stepStyle?.color, Colors.white);
|
||||
expect(firstStep.stepStyle?.connectorColor, Colors.red);
|
||||
expect(firstStep.stepStyle?.indexStyle?.color, Colors.black);
|
||||
expect(firstStep.stepStyle?.indexStyle?.fontSize, 20);
|
||||
expect(firstStep.stepStyle?.border, Border.all(width: 2));
|
||||
|
||||
// Check that the second step has the correct properties.
|
||||
final Step secondStep = stepper.steps[1];
|
||||
expect(secondStep.title, isA<SizedBox>());
|
||||
expect(secondStep.content, isA<SizedBox>());
|
||||
expect(secondStep.isActive, true);
|
||||
expect(secondStep.stepStyle?.connectorThickness, 10);
|
||||
expect(secondStep.stepStyle?.connectorColor, Colors.orange);
|
||||
expect(secondStep.stepStyle?.gradient, const LinearGradient(
|
||||
colors: <Color>[
|
||||
Colors.white,
|
||||
Colors.black,
|
||||
],
|
||||
));
|
||||
|
||||
// Check that the third step has the correct properties.
|
||||
final Step thirdStep = stepper.steps[2];
|
||||
expect(thirdStep.title, isA<SizedBox>());
|
||||
expect(thirdStep.content, isA<SizedBox>());
|
||||
expect(thirdStep.isActive, true);
|
||||
expect(thirdStep.stepStyle?.connectorThickness, 10);
|
||||
expect(thirdStep.stepStyle?.color, Colors.white);
|
||||
expect(thirdStep.stepStyle?.connectorColor, Colors.blue);
|
||||
expect(thirdStep.stepStyle?.indexStyle?.color, Colors.black);
|
||||
expect(thirdStep.stepStyle?.indexStyle?.fontSize, 20);
|
||||
expect(thirdStep.stepStyle?.border, Border.all(width: 2));
|
||||
|
||||
// Check that the fourth step has the correct properties.
|
||||
final Step fourthStep = stepper.steps[3];
|
||||
expect(fourthStep.title, isA<SizedBox>());
|
||||
expect(fourthStep.content, isA<SizedBox>());
|
||||
expect(fourthStep.isActive, true);
|
||||
expect(fourthStep.stepStyle?.color, Colors.white);
|
||||
expect(fourthStep.stepStyle?.indexStyle?.color, Colors.black);
|
||||
expect(fourthStep.stepStyle?.indexStyle?.fontSize, 20);
|
||||
expect(fourthStep.stepStyle?.border, Border.all(width: 2));
|
||||
});
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'button_style.dart';
|
||||
@ -68,6 +69,7 @@ class ControlsDetails {
|
||||
this.onStepCancel,
|
||||
this.onStepContinue,
|
||||
});
|
||||
|
||||
/// Index that is active for the surrounding [Stepper] widget. This may be
|
||||
/// different from [stepIndex] if the user has just changed steps and we are
|
||||
/// currently animating toward that step.
|
||||
@ -121,7 +123,9 @@ 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; // Triangle height. sqrt(3.0) / 2.0
|
||||
const double _kTriangleSqrt = 0.866025; // sqrt(3.0) / 2.0
|
||||
const double _kTriangleHeight = _kStepSize * _kTriangleSqrt;
|
||||
const double _kMaxStepSize = 80.0;
|
||||
|
||||
/// A material step used in [Stepper]. The step can have a title and subtitle,
|
||||
/// an icon within its circle, some content and a state that governs its
|
||||
@ -141,6 +145,7 @@ class Step {
|
||||
this.state = StepState.indexed,
|
||||
this.isActive = false,
|
||||
this.label,
|
||||
this.stepStyle,
|
||||
});
|
||||
|
||||
/// The title of the step that typically describes it.
|
||||
@ -167,6 +172,9 @@ class Step {
|
||||
/// Only [StepperType.horizontal], Optional widget that appears under the [title].
|
||||
/// By default, uses the `bodyLarge` theme.
|
||||
final Widget? label;
|
||||
|
||||
/// Optional overrides for the step's default visual configuration.
|
||||
final StepStyle? stepStyle;
|
||||
}
|
||||
|
||||
/// A material stepper widget that displays progress through a sequence of
|
||||
@ -211,7 +219,18 @@ class Stepper extends StatefulWidget {
|
||||
this.connectorColor,
|
||||
this.connectorThickness,
|
||||
this.stepIconBuilder,
|
||||
}) : assert(0 <= currentStep && currentStep < steps.length);
|
||||
this.stepIconHeight,
|
||||
this.stepIconWidth,
|
||||
this.stepIconMargin,
|
||||
}) : assert(0 <= currentStep && currentStep < steps.length),
|
||||
assert(stepIconHeight == null || (stepIconHeight >= _kStepSize && stepIconHeight <= _kMaxStepSize),
|
||||
'stepIconHeight must be greater than $_kStepSize and less or equal to $_kMaxStepSize'),
|
||||
assert(stepIconWidth == null || (stepIconWidth >= _kStepSize && stepIconWidth <= _kMaxStepSize),
|
||||
'stepIconWidth must be greater than $_kStepSize and less or equal to $_kMaxStepSize'),
|
||||
assert(
|
||||
stepIconHeight == null || stepIconWidth == null || stepIconHeight == stepIconWidth,
|
||||
'If either stepIconHeight or stepIconWidth is specified, both must be specified and '
|
||||
'the values must be equal.');
|
||||
|
||||
/// The steps of the stepper whose titles, subtitles, icons always get shown.
|
||||
///
|
||||
@ -338,6 +357,15 @@ class Stepper extends StatefulWidget {
|
||||
/// If null, the default icons will be used for respective [StepState].
|
||||
final StepIconBuilder? stepIconBuilder;
|
||||
|
||||
/// Overrides the default step icon size height.
|
||||
final double? stepIconHeight;
|
||||
|
||||
/// Overrides the default step icon size width.
|
||||
final double? stepIconWidth;
|
||||
|
||||
/// Overrides the default step icon margin.
|
||||
final EdgeInsets? stepIconMargin;
|
||||
|
||||
@override
|
||||
State<Stepper> createState() => _StepperState();
|
||||
}
|
||||
@ -369,6 +397,16 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
}
|
||||
}
|
||||
|
||||
EdgeInsetsGeometry? get _stepIconMargin => widget.stepIconMargin;
|
||||
|
||||
double? get _stepIconHeight => widget.stepIconHeight;
|
||||
|
||||
double? get _stepIconWidth => widget.stepIconWidth;
|
||||
|
||||
double get _heightFactor {
|
||||
return (_isLabel() && _stepIconHeight != null) ? 2.5 : 2.0;
|
||||
}
|
||||
|
||||
bool _isFirst(int index) {
|
||||
return index == 0;
|
||||
}
|
||||
@ -394,6 +432,10 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
return false;
|
||||
}
|
||||
|
||||
StepStyle? _stepStyle(int index) {
|
||||
return widget.steps[index].stepStyle;
|
||||
}
|
||||
|
||||
Color _connectorColor(bool isActive) {
|
||||
final ColorScheme colorScheme = Theme.of(context).colorScheme;
|
||||
final Set<MaterialState> states = <MaterialState>{
|
||||
@ -421,12 +463,15 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
if (icon != null) {
|
||||
return icon;
|
||||
}
|
||||
TextStyle? textStyle = _stepStyle(index)?.indexStyle;
|
||||
textStyle ??= isDarkActive ? _kStepStyle.copyWith(color: Colors.black87) : _kStepStyle;
|
||||
|
||||
switch (state) {
|
||||
case StepState.indexed:
|
||||
case StepState.disabled:
|
||||
return Text(
|
||||
'${index + 1}',
|
||||
style: isDarkActive ? _kStepStyle.copyWith(color: Colors.black87) : _kStepStyle,
|
||||
style: textStyle,
|
||||
);
|
||||
case StepState.editing:
|
||||
return Icon(
|
||||
@ -441,7 +486,7 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
size: 18.0,
|
||||
);
|
||||
case StepState.error:
|
||||
return const Text('!', style: _kStepStyle);
|
||||
return const Center(child: Text('!', style: _kStepStyle));
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,15 +509,18 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
|
||||
Widget _buildCircle(int index, bool oldState) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
width: _kStepSize,
|
||||
height: _kStepSize,
|
||||
margin:_stepIconMargin ?? const EdgeInsets.symmetric(vertical: 8.0),
|
||||
width: _stepIconWidth ?? _kStepSize,
|
||||
height: _stepIconHeight ?? _kStepSize,
|
||||
child: AnimatedContainer(
|
||||
curve: Curves.fastOutSlowIn,
|
||||
duration: kThemeAnimationDuration,
|
||||
decoration: BoxDecoration(
|
||||
color: _circleColor(index),
|
||||
color: _stepStyle(index)?.color ?? _circleColor(index),
|
||||
shape: BoxShape.circle,
|
||||
border: _stepStyle(index)?.border,
|
||||
boxShadow: _stepStyle(index)?.boxShadow != null ? <BoxShadow>[_stepStyle(index)!.boxShadow!] : null,
|
||||
gradient: _stepStyle(index)?.gradient,
|
||||
),
|
||||
child: Center(
|
||||
child: _buildCircleChild(index, oldState && widget.steps[index].state == StepState.error),
|
||||
@ -482,17 +530,20 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
}
|
||||
|
||||
Widget _buildTriangle(int index, bool oldState) {
|
||||
Color? color = _stepStyle(index)?.errorColor;
|
||||
color ??= _isDark() ? _kErrorDark : _kErrorLight;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
width: _kStepSize,
|
||||
height: _kStepSize,
|
||||
margin: _stepIconMargin ?? const EdgeInsets.symmetric(vertical: 8.0),
|
||||
width: _stepIconWidth ?? _kStepSize,
|
||||
height: _stepIconHeight ?? _kStepSize,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: _kStepSize,
|
||||
height: _kTriangleHeight, // Height of 24dp-long-sided equilateral triangle.
|
||||
width: _stepIconWidth ?? _kStepSize,
|
||||
height: _stepIconHeight != null ? _stepIconHeight! * _kTriangleSqrt : _kTriangleHeight,
|
||||
child: CustomPaint(
|
||||
painter: _TrianglePainter(
|
||||
color: _isDark() ? _kErrorDark : _kErrorLight,
|
||||
color: color,
|
||||
),
|
||||
child: Align(
|
||||
alignment: const Alignment(0.0, 0.8), // 0.8 looks better than the geometrical 0.33.
|
||||
@ -724,14 +775,25 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
}
|
||||
|
||||
Widget _buildVerticalBody(int index) {
|
||||
final double? marginLeft = _stepIconMargin?.resolve(TextDirection.ltr).left;
|
||||
final double? marginRight = _stepIconMargin?.resolve(TextDirection.ltr).right;
|
||||
final double? additionalMarginLeft = marginLeft != null ? marginLeft / 2.0 : null;
|
||||
final double? additionalMarginRight = marginRight != null ? marginRight / 2.0 : null;
|
||||
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
PositionedDirectional(
|
||||
start: 24.0,
|
||||
// When use margin affects the left or right side of the child, we
|
||||
// need to add half of the margin to the start or end of the child
|
||||
// respectively to get the correct positioning.
|
||||
start: 24.0 + (additionalMarginLeft ?? 0.0) + (additionalMarginRight ?? 0.0),
|
||||
top: 0.0,
|
||||
bottom: 0.0,
|
||||
child: SizedBox(
|
||||
width: 24.0,
|
||||
// The line is drawn from the center of the circle vertically until
|
||||
// it reaches the bottom and then horizontally to the edge of the
|
||||
// stepper.
|
||||
width: _stepIconWidth ?? _kStepSize,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: widget.connectorThickness ?? 1.0,
|
||||
@ -745,8 +807,10 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
AnimatedCrossFade(
|
||||
firstChild: Container(height: 0.0),
|
||||
secondChild: Container(
|
||||
margin: widget.margin ?? const EdgeInsetsDirectional.only(
|
||||
start: 60.0,
|
||||
margin: EdgeInsetsDirectional.only(
|
||||
// Adjust [controlsBuilder] padding so that the content is
|
||||
// centered vertically.
|
||||
start: 60.0 + (marginLeft ?? 0.0),
|
||||
end: 24.0,
|
||||
bottom: 24.0,
|
||||
),
|
||||
@ -821,7 +885,7 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsetsDirectional.only(start: 12.0),
|
||||
margin: _stepIconMargin ?? const EdgeInsetsDirectional.only(start: 12.0),
|
||||
child: _buildHeaderText(i),
|
||||
),
|
||||
],
|
||||
@ -831,9 +895,9 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
Expanded(
|
||||
child: Container(
|
||||
key: Key('line$i'),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
height: widget.connectorThickness ?? 1.0,
|
||||
color: _connectorColor(widget.steps[i+1].isActive),
|
||||
margin: _stepIconMargin ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
height: widget.steps[i].stepStyle?.connectorThickness ?? widget.connectorThickness ?? 1.0,
|
||||
color: widget.steps[i].stepStyle?.connectorColor ?? _connectorColor(widget.steps[i].isActive),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -856,6 +920,7 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
||||
elevation: widget.elevation ?? 2,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
height: _stepIconHeight != null ? _stepIconHeight! * _heightFactor : null,
|
||||
child: Row(
|
||||
children: children,
|
||||
),
|
||||
@ -938,3 +1003,170 @@ class _TrianglePainter extends CustomPainter {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// This class is used to override the default visual properties of [Step] widgets within a [Stepper].
|
||||
///
|
||||
/// To customize the appearance of a [Step] create an instance of this class with non-null parameters
|
||||
/// for the step properties whose default value you want to override.
|
||||
///
|
||||
/// Example usage:
|
||||
/// ```dart
|
||||
/// Step(
|
||||
/// title: const Text('Step 1'),
|
||||
/// content: const Text('Content for Step 1'),
|
||||
/// stepStyle: StepStyle(
|
||||
/// color: Colors.blue,
|
||||
/// errorColor: Colors.red,
|
||||
/// border: Border.all(color: Colors.grey),
|
||||
/// boxShadow: const BoxShadow(blurRadius: 3.0, color: Colors.black26),
|
||||
/// gradient: const LinearGradient(colors: <Color>[Colors.red, Colors.blue]),
|
||||
/// indexStyle: const TextStyle(color: Colors.white),
|
||||
/// ),
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// An example that uses [StepStyle] to customize the appearance of each [Step] in a [Stepper].
|
||||
///
|
||||
/// ** See code in examples/api/lib/material/stepper/step_style.0.dart **
|
||||
/// {@end-tool}
|
||||
|
||||
@immutable
|
||||
class StepStyle with Diagnosticable {
|
||||
/// Constructs a [StepStyle].
|
||||
const StepStyle({
|
||||
this.color,
|
||||
this.errorColor,
|
||||
this.connectorColor,
|
||||
this.connectorThickness,
|
||||
this.border,
|
||||
this.boxShadow,
|
||||
this.gradient,
|
||||
this.indexStyle,
|
||||
});
|
||||
|
||||
/// Overrides the default color of the circle in the step.
|
||||
final Color? color;
|
||||
|
||||
/// Overrides the default color of the error indicator in the step.
|
||||
final Color? errorColor;
|
||||
|
||||
/// Overrides the default color of the connector line between two steps.
|
||||
///
|
||||
/// This property only applies when [Stepper.type] is [StepperType.horizontal].
|
||||
final Color? connectorColor;
|
||||
|
||||
/// Overrides the default thickness of the connector line between two steps.
|
||||
///
|
||||
/// This property only applies when [Stepper.type] is [StepperType.horizontal].
|
||||
final double? connectorThickness;
|
||||
|
||||
/// Add a border around the step.
|
||||
///
|
||||
/// Will be applied to the circle in the step.
|
||||
final BoxBorder? border;
|
||||
|
||||
/// Add a shadow around the step.
|
||||
final BoxShadow? boxShadow;
|
||||
|
||||
/// Add a gradient around the step.
|
||||
///
|
||||
/// If [gradient] is specified, [color] will be ignored.
|
||||
final Gradient? gradient;
|
||||
|
||||
/// Overrides the default style of the index in the step.
|
||||
final TextStyle? indexStyle;
|
||||
|
||||
/// Returns a copy of this ButtonStyle with the given fields replaced with
|
||||
/// the new values.
|
||||
StepStyle copyWith({
|
||||
Color? color,
|
||||
Color? errorColor,
|
||||
Color? connectorColor,
|
||||
double? connectorThickness,
|
||||
BoxBorder? border,
|
||||
BoxShadow? boxShadow,
|
||||
Gradient? gradient,
|
||||
TextStyle? indexStyle,
|
||||
}) {
|
||||
return StepStyle(
|
||||
color: color ?? this.color,
|
||||
errorColor: errorColor ?? this.errorColor,
|
||||
connectorColor: connectorColor ?? this.connectorColor,
|
||||
connectorThickness: connectorThickness ?? this.connectorThickness,
|
||||
border: border ?? this.border,
|
||||
boxShadow: boxShadow ?? this.boxShadow,
|
||||
gradient: gradient ?? this.gradient,
|
||||
indexStyle: indexStyle ?? this.indexStyle,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns a copy of this StepStyle where the non-null fields in [stepStyle]
|
||||
/// have replaced the corresponding null fields in this StepStyle.
|
||||
///
|
||||
/// In other words, [stepStyle] is used to fill in unspecified (null) fields
|
||||
/// this StepStyle.
|
||||
StepStyle merge(StepStyle? stepStyle) {
|
||||
if (stepStyle == null) {
|
||||
return this;
|
||||
}
|
||||
return copyWith(
|
||||
color: stepStyle.color,
|
||||
errorColor: stepStyle.errorColor,
|
||||
connectorColor: stepStyle.connectorColor,
|
||||
connectorThickness: stepStyle.connectorThickness,
|
||||
border: stepStyle.border,
|
||||
boxShadow: stepStyle.boxShadow,
|
||||
gradient: stepStyle.gradient,
|
||||
indexStyle: stepStyle.indexStyle,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
color,
|
||||
errorColor,
|
||||
connectorColor,
|
||||
connectorThickness,
|
||||
border,
|
||||
boxShadow,
|
||||
gradient,
|
||||
indexStyle,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other.runtimeType != runtimeType) {
|
||||
return false;
|
||||
}
|
||||
return other is StepStyle &&
|
||||
other.color == color &&
|
||||
other.errorColor == errorColor &&
|
||||
other.connectorColor == connectorColor &&
|
||||
other.connectorThickness == connectorThickness &&
|
||||
other.border == border &&
|
||||
other.boxShadow == boxShadow &&
|
||||
other.gradient == gradient &&
|
||||
other.indexStyle == indexStyle;
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
final ThemeData theme = ThemeData.fallback();
|
||||
final TextTheme defaultTextTheme = theme.textTheme;
|
||||
properties.add(ColorProperty('color', color, defaultValue: null));
|
||||
properties.add(ColorProperty('errorColor', errorColor, defaultValue: null));
|
||||
properties.add(ColorProperty('connectorColor', connectorColor, defaultValue: null));
|
||||
properties.add(DoubleProperty('connectorThickness', connectorThickness, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<BoxBorder>('border', border, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<BoxShadow>('boxShadow', boxShadow, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<Gradient>('gradient', gradient, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<TextStyle>('indexStyle', indexStyle, defaultValue: defaultTextTheme.bodyLarge));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1530,7 +1530,7 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async
|
||||
expect(circleColor('1'), selectedColor);
|
||||
expect(circleColor('2'), disabledColor);
|
||||
// in two steps case there will be single line
|
||||
expect(lineColor('line0'), disabledColor);
|
||||
expect(lineColor('line0'), selectedColor);
|
||||
|
||||
// now hitting step two
|
||||
await tester.tap(find.text('step2'));
|
||||
@ -1587,6 +1587,108 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async
|
||||
expect(find.text('!'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('StepperProperties test', (WidgetTester tester) async {
|
||||
const Widget widget = SizedBox.shrink();
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: Stepper(
|
||||
stepIconHeight: 24,
|
||||
stepIconWidth: 24,
|
||||
stepIconMargin: const EdgeInsets.all(8),
|
||||
steps: List<Step>.generate(3, (int index) {
|
||||
return Step(
|
||||
title: Text('Step $index'),
|
||||
content: widget,
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final Finder stepperFinder = find.byType(Stepper);
|
||||
final Stepper stepper = tester.widget<Stepper>(stepperFinder);
|
||||
|
||||
expect(stepper.stepIconHeight, 24);
|
||||
expect(stepper.stepIconWidth, 24);
|
||||
expect(stepper.stepIconMargin, const EdgeInsets.all(8));
|
||||
});
|
||||
|
||||
testWidgets('StepStyle test', (WidgetTester tester) async {
|
||||
final StepStyle stepStyle = StepStyle(
|
||||
color: Colors.white,
|
||||
errorColor: Colors.orange,
|
||||
connectorColor: Colors.red,
|
||||
connectorThickness: 2,
|
||||
border: Border.all(),
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Colors.red, Colors.blue],
|
||||
),
|
||||
indexStyle: const TextStyle(color: Colors.black),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: Stepper(
|
||||
steps: <Step>[
|
||||
Step(
|
||||
title: const Text('Regular title'),
|
||||
content: const Text('Text content'),
|
||||
stepStyle: stepStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final Finder stepperFinder = find.byType(Stepper);
|
||||
final Stepper stepper = tester.widget<Stepper>(stepperFinder);
|
||||
final StepStyle? style = stepper.steps.first.stepStyle;
|
||||
|
||||
expect(style?.color, stepStyle.color);
|
||||
expect(style?.errorColor, stepStyle.errorColor);
|
||||
expect(style?.connectorColor, stepStyle.connectorColor);
|
||||
expect(style?.connectorThickness, stepStyle.connectorThickness);
|
||||
expect(style?.border, stepStyle.border);
|
||||
expect(style?.gradient, stepStyle.gradient);
|
||||
expect(style?.indexStyle, stepStyle.indexStyle);
|
||||
|
||||
//copyWith
|
||||
final StepStyle newStyle = stepStyle.copyWith(
|
||||
color: Colors.black,
|
||||
errorColor: Colors.red,
|
||||
connectorColor: Colors.blue,
|
||||
connectorThickness: 3,
|
||||
border: Border.all(),
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Colors.red, Colors.blue],
|
||||
),
|
||||
indexStyle: const TextStyle(color: Colors.black),
|
||||
);
|
||||
|
||||
expect(newStyle.color, Colors.black);
|
||||
expect(newStyle.errorColor, Colors.red);
|
||||
expect(newStyle.connectorColor, Colors.blue);
|
||||
expect(newStyle.connectorThickness, 3);
|
||||
expect(newStyle.border, stepStyle.border);
|
||||
expect(newStyle.gradient, stepStyle.gradient);
|
||||
expect(newStyle.indexStyle, stepStyle.indexStyle);
|
||||
|
||||
//merge
|
||||
final StepStyle mergedStyle = stepStyle.merge(newStyle);
|
||||
|
||||
expect(mergedStyle.color, Colors.black);
|
||||
expect(mergedStyle.errorColor, Colors.red);
|
||||
expect(mergedStyle.connectorColor, Colors.blue);
|
||||
expect(mergedStyle.connectorThickness, 3);
|
||||
expect(mergedStyle.border, stepStyle.border);
|
||||
expect(mergedStyle.gradient, stepStyle.gradient);
|
||||
expect(mergedStyle.indexStyle, stepStyle.indexStyle);
|
||||
});
|
||||
}
|
||||
|
||||
class _TappableColorWidget extends StatefulWidget {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user