Add ability to clip Stepper
step content (#152370)
fixes [Dismissible content overlays Stepper interface while dismissing it](https://github.com/flutter/flutter/issues/66007) ### Code sample <details> <summary>expand to view the code sample</summary> ```dart import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final List<String> items = List<String>.generate(20, (int i) => 'Item ${i + 1}'); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Padding( padding: const EdgeInsets.all(20), child: DecoratedBox( decoration: BoxDecoration( border: Border.all(color: Colors.amber, width: 2), ), child: Padding( padding: const EdgeInsets.all(2.0), child: Column( children: <Widget>[ const SizedBox(height: 8.0), Text( 'Dismissible Widget - Vertical Stepper Widget', style: Theme.of(context).textTheme.titleLarge, ), Expanded( child: Stepper( clipBehavior: Clip.hardEdge, steps: <Step>[ Step( isActive: true, title: const Text('Step 1'), content: ColoredBox( color: Colors.black12, child: ListView.builder( itemCount: items.length, shrinkWrap: true, itemBuilder: (BuildContext context, int index) { final String item = items[index]; return Dismissible( key: Key(item), onDismissed: (DismissDirection direction) { setState(() { items.removeAt(index); }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('$item dismissed'))); }, background: Container(color: Colors.red), child: ListTile(title: Text(item)), ); }, ), ), ), const Step( title: Text('Step 2'), content: Text('content'), ), ], ), ), const Divider(height: 1), const SizedBox(height: 8.0), Text( 'Dismissible Widget - Horizontal Stepper Widget', style: Theme.of(context).textTheme.titleLarge, ), Expanded( child: Stepper( clipBehavior: Clip.hardEdge, type: StepperType.horizontal, elevation: 0.0, steps: <Step>[ Step( isActive: true, title: const Text('Step 1'), content: ColoredBox( color: Colors.black12, child: ListView.builder( itemCount: items.length, shrinkWrap: true, itemBuilder: (BuildContext context, int index) { final String item = items[index]; return Dismissible( key: Key(item), onDismissed: (DismissDirection direction) { setState(() { items.removeAt(index); }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('$item dismissed'))); }, background: Container(color: Colors.red), child: ListTile(title: Text(item)), ); }, ), ), ), const Step( title: Text('Step 2'), content: Text('content'), ), ], ), ), ], ), ), ), ), ), ); } } ``` </details> ### Without `Stepper` step content clipping  ### With `Stepper` step content clipping 
This commit is contained in:
parent
91a3f69f11
commit
0d154e55d1
@ -222,6 +222,7 @@ class Stepper extends StatefulWidget {
|
|||||||
this.stepIconHeight,
|
this.stepIconHeight,
|
||||||
this.stepIconWidth,
|
this.stepIconWidth,
|
||||||
this.stepIconMargin,
|
this.stepIconMargin,
|
||||||
|
this.clipBehavior = Clip.none,
|
||||||
}) : assert(0 <= currentStep && currentStep < steps.length),
|
}) : assert(0 <= currentStep && currentStep < steps.length),
|
||||||
assert(stepIconHeight == null || (stepIconHeight >= _kStepSize && stepIconHeight <= _kMaxStepSize),
|
assert(stepIconHeight == null || (stepIconHeight >= _kStepSize && stepIconHeight <= _kMaxStepSize),
|
||||||
'stepIconHeight must be greater than $_kStepSize and less or equal to $_kMaxStepSize'),
|
'stepIconHeight must be greater than $_kStepSize and less or equal to $_kMaxStepSize'),
|
||||||
@ -366,6 +367,15 @@ class Stepper extends StatefulWidget {
|
|||||||
/// Overrides the default step icon margin.
|
/// Overrides the default step icon margin.
|
||||||
final EdgeInsets? stepIconMargin;
|
final EdgeInsets? stepIconMargin;
|
||||||
|
|
||||||
|
/// The [Step.content] will be clipped to this Clip type.
|
||||||
|
///
|
||||||
|
/// Defaults to [Clip.none].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [Clip], which explains how to use this property.
|
||||||
|
final Clip clipBehavior;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<Stepper> createState() => _StepperState();
|
State<Stepper> createState() => _StepperState();
|
||||||
}
|
}
|
||||||
@ -795,7 +805,10 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
widget.steps[index].content,
|
ClipRect(
|
||||||
|
clipBehavior: widget.clipBehavior,
|
||||||
|
child: widget.steps[index].content,
|
||||||
|
),
|
||||||
_buildVerticalControls(index),
|
_buildVerticalControls(index),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -888,7 +901,10 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
|||||||
Visibility(
|
Visibility(
|
||||||
maintainState: true,
|
maintainState: true,
|
||||||
visible: i == widget.currentStep,
|
visible: i == widget.currentStep,
|
||||||
child: widget.steps[i].content,
|
child: ClipRect(
|
||||||
|
clipBehavior: widget.clipBehavior,
|
||||||
|
child: widget.steps[i].content,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1720,6 +1720,91 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async
|
|||||||
));
|
));
|
||||||
expect(lastConnector.width, equals(0.0));
|
expect(lastConnector.width, equals(0.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// This is a regression test for https://github.com/flutter/flutter/issues/66007.
|
||||||
|
testWidgets('Default Stepper clipBehavior', (WidgetTester tester) async {
|
||||||
|
Widget buildStepper({ required StepperType type }) {
|
||||||
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Stepper(
|
||||||
|
type: type,
|
||||||
|
steps: const <Step>[
|
||||||
|
Step(
|
||||||
|
title: Text('step1'),
|
||||||
|
content: Text('step1 content'),
|
||||||
|
),
|
||||||
|
Step(
|
||||||
|
title: Text('step2'),
|
||||||
|
content: Text('step2 content'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClipRect getContentClipRect() {
|
||||||
|
return tester.widget<ClipRect>(find.ancestor(
|
||||||
|
of: find.text('step1 content'),
|
||||||
|
matching: find.byType(ClipRect),
|
||||||
|
).first);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vertical stepper with default clipBehavior.
|
||||||
|
await tester.pumpWidget(buildStepper(type: StepperType.vertical));
|
||||||
|
|
||||||
|
expect(getContentClipRect().clipBehavior, equals(Clip.none));
|
||||||
|
|
||||||
|
// Test horizontal stepper with default clipBehavior.
|
||||||
|
await tester.pumpWidget(buildStepper(type: StepperType.horizontal));
|
||||||
|
|
||||||
|
expect(getContentClipRect().clipBehavior, equals(Clip.none));
|
||||||
|
});
|
||||||
|
|
||||||
|
// This is a regression test for https://github.com/flutter/flutter/issues/66007.
|
||||||
|
testWidgets('Stepper steps can be clipped', (WidgetTester tester) async {
|
||||||
|
Widget buildStepper({ required StepperType type, required Clip clipBehavior }) {
|
||||||
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Stepper(
|
||||||
|
clipBehavior: clipBehavior,
|
||||||
|
type: type,
|
||||||
|
steps: const <Step>[
|
||||||
|
Step(
|
||||||
|
title: Text('step1'),
|
||||||
|
content: Text('step1 content'),
|
||||||
|
),
|
||||||
|
Step(
|
||||||
|
title: Text('step2'),
|
||||||
|
content: Text('step2 content'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClipRect getContentClipRect() {
|
||||||
|
return tester.widget<ClipRect>(find.ancestor(
|
||||||
|
of: find.text('step1 content'),
|
||||||
|
matching: find.byType(ClipRect),
|
||||||
|
).first);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vertical stepper with clipBehavior set to Clip.hardEdge.
|
||||||
|
await tester.pumpWidget(buildStepper(type: StepperType.vertical, clipBehavior: Clip.hardEdge));
|
||||||
|
|
||||||
|
expect(getContentClipRect().clipBehavior, equals(Clip.hardEdge));
|
||||||
|
|
||||||
|
// Test horizontal stepper with clipBehavior set to Clip.hardEdge.
|
||||||
|
await tester.pumpWidget(buildStepper(type: StepperType.horizontal, clipBehavior: Clip.hardEdge));
|
||||||
|
|
||||||
|
expect(getContentClipRect().clipBehavior, equals(Clip.hardEdge));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TappableColorWidget extends StatefulWidget {
|
class _TappableColorWidget extends StatefulWidget {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user