mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
WIP Commits separated as follows: - Update lints in analysis_options files - Run `dart fix --apply` - Clean up leftover analysis issues - Run `dart format .` in the right places. Local analysis and testing passes. Checking CI now. Part of https://github.com/flutter/flutter/issues/178827 - Adoption of flutter_lints in examples/api coming in a separate change (cc @loic-sharma) ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
829 lines
27 KiB
Dart
829 lines
27 KiB
Dart
// 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 'dart:ui' as ui;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
// The const represents the starting position of the scrollbar thumb for
|
|
// the below tests. The thumb is 90 pixels long, and 8 pixels wide, with a 2
|
|
// pixel margin to the right edge of the viewport.
|
|
const Rect _kMaterialDesignInitialThumbRect = Rect.fromLTRB(790.0, 0.0, 798.0, 90.0);
|
|
const Radius _kDefaultThumbRadius = Radius.circular(8.0);
|
|
const Color _kDefaultIdleThumbColor = Color(0x1a000000);
|
|
const Color _kDefaultDragThumbColor = Color(0x99000000);
|
|
|
|
void main() {
|
|
test('ScrollbarThemeData copyWith, ==, hashCode basics', () {
|
|
expect(const ScrollbarThemeData(), const ScrollbarThemeData().copyWith());
|
|
expect(const ScrollbarThemeData().hashCode, const ScrollbarThemeData().copyWith().hashCode);
|
|
});
|
|
|
|
test('ScrollbarThemeData lerp special cases', () {
|
|
expect(ScrollbarThemeData.lerp(null, null, 0), const ScrollbarThemeData());
|
|
const data = ScrollbarThemeData();
|
|
expect(identical(ScrollbarThemeData.lerp(data, data, 0.5), data), true);
|
|
});
|
|
|
|
testWidgets(
|
|
'Passing no ScrollbarTheme returns defaults',
|
|
(WidgetTester tester) async {
|
|
final scrollController = ScrollController();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
useMaterial3: false,
|
|
scrollbarTheme: ScrollbarThemeData(
|
|
trackVisibility: WidgetStateProperty.resolveWith(
|
|
(Set<WidgetState> states) => states.contains(WidgetState.hovered),
|
|
),
|
|
),
|
|
),
|
|
home: ScrollConfiguration(
|
|
behavior: const NoScrollbarBehavior(),
|
|
child: Scrollbar(
|
|
thumbVisibility: true,
|
|
controller: scrollController,
|
|
child: SingleChildScrollView(
|
|
controller: scrollController,
|
|
child: const SizedBox(width: 4000.0, height: 4000.0),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// Idle scrollbar behavior
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(_kMaterialDesignInitialThumbRect, _kDefaultThumbRadius),
|
|
color: _kDefaultIdleThumbColor,
|
|
),
|
|
);
|
|
|
|
// Drag scrollbar behavior
|
|
const scrollAmount = 10.0;
|
|
final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(_kMaterialDesignInitialThumbRect, _kDefaultThumbRadius),
|
|
// Drag color
|
|
color: _kDefaultDragThumbColor,
|
|
),
|
|
);
|
|
|
|
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
|
|
await tester.pumpAndSettle();
|
|
await dragScrollbarGesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
// Hover scrollbar behavior
|
|
final TestGesture gesture = await tester.createGesture(kind: ui.PointerDeviceKind.mouse);
|
|
await gesture.addPointer();
|
|
await gesture.moveTo(const Offset(794.0, 5.0));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints
|
|
..rect(
|
|
rect: const Rect.fromLTRB(784.0, 0.0, 800.0, 600.0),
|
|
color: const Color(0x08000000),
|
|
)
|
|
..line(
|
|
p1: const Offset(784.0, 0.0),
|
|
p2: const Offset(784.0, 600.0),
|
|
strokeWidth: 1.0,
|
|
color: const Color(0x1a000000),
|
|
)
|
|
..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
// Scrollbar thumb is larger
|
|
const Rect.fromLTRB(786.0, 10.0, 798.0, 100.0),
|
|
_kDefaultThumbRadius,
|
|
),
|
|
// Hover color
|
|
color: const Color(0x80000000),
|
|
),
|
|
);
|
|
|
|
scrollController.dispose();
|
|
},
|
|
variant: const TargetPlatformVariant(<TargetPlatform>{
|
|
TargetPlatform.linux,
|
|
TargetPlatform.macOS,
|
|
TargetPlatform.windows,
|
|
TargetPlatform.fuchsia,
|
|
}),
|
|
);
|
|
|
|
testWidgets(
|
|
'Scrollbar uses values from ScrollbarTheme',
|
|
(WidgetTester tester) async {
|
|
final ScrollbarThemeData scrollbarTheme = _scrollbarTheme();
|
|
final scrollController = ScrollController();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(scrollbarTheme: scrollbarTheme),
|
|
home: ScrollConfiguration(
|
|
behavior: const NoScrollbarBehavior(),
|
|
child: Scrollbar(
|
|
thumbVisibility: true,
|
|
controller: scrollController,
|
|
child: SingleChildScrollView(
|
|
controller: scrollController,
|
|
child: const SizedBox(width: 4000.0, height: 4000.0),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// Idle scrollbar behavior
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
const Rect.fromLTRB(785.0, 10.0, 795.0, 97.0),
|
|
const Radius.circular(6.0),
|
|
),
|
|
color: const Color(0xff4caf50),
|
|
),
|
|
);
|
|
|
|
// Drag scrollbar behavior
|
|
const scrollAmount = 10.0;
|
|
final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
const Rect.fromLTRB(785.0, 10.0, 795.0, 97.0),
|
|
const Radius.circular(6.0),
|
|
),
|
|
// Drag color
|
|
color: const Color(0xfff44336),
|
|
),
|
|
);
|
|
|
|
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
|
|
await tester.pumpAndSettle();
|
|
await dragScrollbarGesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
// Hover scrollbar behavior
|
|
final TestGesture gesture = await tester.createGesture(kind: ui.PointerDeviceKind.mouse);
|
|
await gesture.addPointer();
|
|
await gesture.moveTo(const Offset(794.0, 15.0));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints
|
|
..rect(
|
|
rect: const Rect.fromLTRB(770.0, 0.0, 800.0, 600.0),
|
|
color: const Color(0xff000000),
|
|
)
|
|
..line(
|
|
p1: const Offset(770.0, 00.0),
|
|
p2: const Offset(770.0, 600.0),
|
|
strokeWidth: 1.0,
|
|
color: const Color(0xffffeb3b),
|
|
)
|
|
..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
// Scrollbar thumb is larger
|
|
const Rect.fromLTRB(775.0, 20.0, 795.0, 107.0),
|
|
const Radius.circular(6.0),
|
|
),
|
|
// Hover color
|
|
color: const Color(0xff2196f3),
|
|
),
|
|
);
|
|
|
|
scrollController.dispose();
|
|
},
|
|
variant: const TargetPlatformVariant(<TargetPlatform>{
|
|
TargetPlatform.linux,
|
|
TargetPlatform.macOS,
|
|
TargetPlatform.windows,
|
|
TargetPlatform.fuchsia,
|
|
}),
|
|
);
|
|
|
|
testWidgets('Scrollbar uses values from ScrollbarTheme if exists instead of values from Theme', (
|
|
WidgetTester tester,
|
|
) async {
|
|
final ScrollbarThemeData scrollbarTheme = _scrollbarTheme();
|
|
final scrollController = ScrollController();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(scrollbarTheme: scrollbarTheme),
|
|
home: ScrollConfiguration(
|
|
behavior: const NoScrollbarBehavior(),
|
|
child: ScrollbarTheme(
|
|
data: _scrollbarTheme().copyWith(
|
|
thumbColor: WidgetStateProperty.all(const Color(0xFF000000)),
|
|
),
|
|
child: Scrollbar(
|
|
thumbVisibility: true,
|
|
controller: scrollController,
|
|
child: SingleChildScrollView(
|
|
controller: scrollController,
|
|
child: const SizedBox(width: 4000.0, height: 4000.0),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// Idle scrollbar behavior
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
const Rect.fromLTRB(785.0, 10.0, 795.0, 97.0),
|
|
const Radius.circular(6.0),
|
|
),
|
|
color: const Color(0xFF000000),
|
|
),
|
|
);
|
|
|
|
scrollController.dispose();
|
|
});
|
|
|
|
testWidgets(
|
|
'ScrollbarTheme can disable gestures',
|
|
(WidgetTester tester) async {
|
|
final scrollController = ScrollController();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
useMaterial3: false,
|
|
scrollbarTheme: const ScrollbarThemeData(interactive: false),
|
|
),
|
|
home: Scrollbar(
|
|
thumbVisibility: true,
|
|
controller: scrollController,
|
|
child: SingleChildScrollView(
|
|
controller: scrollController,
|
|
child: const SizedBox(width: 4000.0, height: 4000.0),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// Idle scrollbar behavior
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(_kMaterialDesignInitialThumbRect, _kDefaultThumbRadius),
|
|
color: _kDefaultIdleThumbColor,
|
|
),
|
|
);
|
|
|
|
// Try to drag scrollbar.
|
|
const scrollAmount = 10.0;
|
|
final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
|
|
await tester.pumpAndSettle();
|
|
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
|
|
await tester.pumpAndSettle();
|
|
await dragScrollbarGesture.up();
|
|
await tester.pumpAndSettle();
|
|
// Expect no change
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(_kMaterialDesignInitialThumbRect, _kDefaultThumbRadius),
|
|
color: _kDefaultIdleThumbColor,
|
|
),
|
|
);
|
|
|
|
scrollController.dispose();
|
|
},
|
|
variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.fuchsia}),
|
|
);
|
|
|
|
testWidgets(
|
|
'Scrollbar.interactive takes priority over ScrollbarTheme',
|
|
(WidgetTester tester) async {
|
|
final scrollController = ScrollController();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
useMaterial3: false,
|
|
scrollbarTheme: const ScrollbarThemeData(interactive: false),
|
|
),
|
|
home: Scrollbar(
|
|
interactive: true,
|
|
thumbVisibility: true,
|
|
controller: scrollController,
|
|
child: SingleChildScrollView(
|
|
controller: scrollController,
|
|
child: const SizedBox(width: 4000.0, height: 4000.0),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// Idle scrollbar behavior
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(_kMaterialDesignInitialThumbRect, _kDefaultThumbRadius),
|
|
color: _kDefaultIdleThumbColor,
|
|
),
|
|
);
|
|
|
|
// Drag scrollbar.
|
|
const scrollAmount = 10.0;
|
|
final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
|
|
await tester.pumpAndSettle();
|
|
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
|
|
await tester.pumpAndSettle();
|
|
await dragScrollbarGesture.up();
|
|
await tester.pumpAndSettle();
|
|
// Gestures handled by Scrollbar.
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
const Rect.fromLTRB(790.0, 10.0, 798.0, 100.0),
|
|
_kDefaultThumbRadius,
|
|
),
|
|
color: _kDefaultIdleThumbColor,
|
|
),
|
|
);
|
|
|
|
scrollController.dispose();
|
|
},
|
|
variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.fuchsia}),
|
|
);
|
|
|
|
testWidgets(
|
|
'Scrollbar widget properties take priority over theme',
|
|
(WidgetTester tester) async {
|
|
const thickness = 4.0;
|
|
const edgeMargin = 2.0;
|
|
const radius = Radius.circular(3.0);
|
|
final scrollController = ScrollController();
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
colorScheme: const ColorScheme.light(),
|
|
scrollbarTheme: ScrollbarThemeData(
|
|
trackVisibility: WidgetStateProperty.resolveWith(
|
|
(Set<WidgetState> states) => states.contains(WidgetState.hovered),
|
|
),
|
|
),
|
|
),
|
|
home: ScrollConfiguration(
|
|
behavior: const NoScrollbarBehavior(),
|
|
child: Scrollbar(
|
|
thickness: thickness,
|
|
thumbVisibility: true,
|
|
radius: radius,
|
|
controller: scrollController,
|
|
child: SingleChildScrollView(
|
|
controller: scrollController,
|
|
child: const SizedBox(width: 4000.0, height: 4000.0),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
// Idle scrollbar behavior
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
const Rect.fromLTRB(800 - thickness - edgeMargin, 0.0, 798.0, 90.0),
|
|
const Radius.circular(3.0),
|
|
),
|
|
color: _kDefaultIdleThumbColor,
|
|
),
|
|
);
|
|
|
|
// Drag scrollbar behavior.
|
|
const scrollAmount = 10.0;
|
|
final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
const Rect.fromLTRB(800 - thickness - edgeMargin, 0.0, 798.0, 90.0),
|
|
const Radius.circular(3.0),
|
|
),
|
|
// Drag color
|
|
color: _kDefaultDragThumbColor,
|
|
),
|
|
);
|
|
|
|
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
|
|
await tester.pumpAndSettle();
|
|
await dragScrollbarGesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
// Hover scrollbar behavior.
|
|
final TestGesture gesture = await tester.createGesture(kind: ui.PointerDeviceKind.mouse);
|
|
await gesture.addPointer();
|
|
await gesture.moveTo(const Offset(794.0, 5.0));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints
|
|
..rect(
|
|
rect: const Rect.fromLTRB(792.0, 0.0, 800.0, 600.0),
|
|
color: const Color(0x08000000),
|
|
)
|
|
..line(
|
|
p1: const Offset(792.0, 0.0),
|
|
p2: const Offset(792.0, 600.0),
|
|
strokeWidth: 1.0,
|
|
color: const Color(0x1a000000),
|
|
)
|
|
..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
const Rect.fromLTRB(800 - thickness - edgeMargin, 10.0, 798.0, 100.0),
|
|
const Radius.circular(3.0),
|
|
),
|
|
// Hover color
|
|
color: const Color(0x80000000),
|
|
),
|
|
);
|
|
|
|
scrollController.dispose();
|
|
},
|
|
variant: const TargetPlatformVariant(<TargetPlatform>{
|
|
TargetPlatform.linux,
|
|
TargetPlatform.macOS,
|
|
TargetPlatform.windows,
|
|
TargetPlatform.fuchsia,
|
|
}),
|
|
);
|
|
|
|
testWidgets(
|
|
'ThemeData colorScheme is used when no ScrollbarTheme is set',
|
|
(WidgetTester tester) async {
|
|
(ScrollController, Widget) buildFrame(ThemeData appTheme) {
|
|
final scrollController = ScrollController();
|
|
final ThemeData theme = appTheme.copyWith(
|
|
scrollbarTheme: ScrollbarThemeData(
|
|
trackVisibility: WidgetStateProperty.resolveWith(
|
|
(Set<WidgetState> states) => states.contains(WidgetState.hovered),
|
|
),
|
|
),
|
|
);
|
|
return (
|
|
scrollController,
|
|
MaterialApp(
|
|
theme: theme,
|
|
home: ScrollConfiguration(
|
|
behavior: const NoScrollbarBehavior(),
|
|
child: Scrollbar(
|
|
thumbVisibility: true,
|
|
controller: scrollController,
|
|
child: SingleChildScrollView(
|
|
controller: scrollController,
|
|
child: const SizedBox(width: 4000.0, height: 4000.0),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Scrollbar defaults for light themes:
|
|
// - coloring based on ColorScheme.onSurface
|
|
final (ScrollController controller1, Widget frame1) = buildFrame(
|
|
ThemeData(colorScheme: const ColorScheme.light()),
|
|
);
|
|
await tester.pumpWidget(frame1);
|
|
await tester.pumpAndSettle();
|
|
// Idle scrollbar behavior
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(_kMaterialDesignInitialThumbRect, _kDefaultThumbRadius),
|
|
color: _kDefaultIdleThumbColor,
|
|
),
|
|
);
|
|
|
|
// Drag scrollbar behavior
|
|
const scrollAmount = 10.0;
|
|
TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(_kMaterialDesignInitialThumbRect, _kDefaultThumbRadius),
|
|
// Drag color
|
|
color: _kDefaultDragThumbColor,
|
|
),
|
|
);
|
|
|
|
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
|
|
await tester.pumpAndSettle();
|
|
await dragScrollbarGesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
// Hover scrollbar behavior
|
|
final TestGesture hoverGesture = await tester.createGesture(kind: ui.PointerDeviceKind.mouse);
|
|
await hoverGesture.addPointer();
|
|
addTearDown(hoverGesture.removePointer);
|
|
await hoverGesture.moveTo(const Offset(794.0, 5.0));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints
|
|
..rect(
|
|
rect: const Rect.fromLTRB(784.0, 0.0, 800.0, 600.0),
|
|
color: const Color(0x08000000),
|
|
)
|
|
..line(
|
|
p1: const Offset(784.0, 0.0),
|
|
p2: const Offset(784.0, 600.0),
|
|
strokeWidth: 1.0,
|
|
color: const Color(0x1a000000),
|
|
)
|
|
..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
// Scrollbar thumb is larger
|
|
const Rect.fromLTRB(786.0, 10.0, 798.0, 100.0),
|
|
_kDefaultThumbRadius,
|
|
),
|
|
// Hover color
|
|
color: const Color(0x80000000),
|
|
),
|
|
);
|
|
|
|
await hoverGesture.moveTo(Offset.zero);
|
|
|
|
// Scrollbar defaults for dark themes:
|
|
// - coloring slightly different based on ColorScheme.onSurface
|
|
final (ScrollController controller2, Widget frame2) = buildFrame(
|
|
ThemeData(colorScheme: const ColorScheme.dark()),
|
|
);
|
|
await tester.pumpWidget(frame2);
|
|
await tester.pumpAndSettle(); // Theme change animation
|
|
|
|
// Idle scrollbar behavior
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
const Rect.fromLTRB(790.0, 10.0, 798.0, 100.0),
|
|
_kDefaultThumbRadius,
|
|
),
|
|
color: const Color(0x4dffffff),
|
|
),
|
|
);
|
|
|
|
// Drag scrollbar behavior
|
|
dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
const Rect.fromLTRB(790.0, 10.0, 798.0, 100.0),
|
|
_kDefaultThumbRadius,
|
|
),
|
|
// Drag color
|
|
color: const Color(0xbfffffff),
|
|
),
|
|
);
|
|
|
|
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
|
|
await tester.pumpAndSettle();
|
|
await dragScrollbarGesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
// Hover scrollbar behavior
|
|
await hoverGesture.moveTo(const Offset(794.0, 5.0));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints
|
|
..rect(
|
|
rect: const Rect.fromLTRB(784.0, 0.0, 800.0, 600.0),
|
|
color: const Color(0x0dffffff),
|
|
)
|
|
..line(
|
|
p1: const Offset(784.0, 0.0),
|
|
p2: const Offset(784.0, 600.0),
|
|
strokeWidth: 1.0,
|
|
color: const Color(0x40ffffff),
|
|
)
|
|
..rrect(
|
|
rrect: RRect.fromRectAndRadius(
|
|
// Scrollbar thumb is larger
|
|
const Rect.fromLTRB(786.0, 20.0, 798.0, 110.0),
|
|
_kDefaultThumbRadius,
|
|
),
|
|
// Hover color
|
|
color: const Color(0xa6ffffff),
|
|
),
|
|
);
|
|
|
|
controller1.dispose();
|
|
controller2.dispose();
|
|
},
|
|
variant: const TargetPlatformVariant(<TargetPlatform>{
|
|
TargetPlatform.linux,
|
|
TargetPlatform.macOS,
|
|
TargetPlatform.windows,
|
|
TargetPlatform.fuchsia,
|
|
}),
|
|
);
|
|
|
|
testWidgets(
|
|
'ScrollbarThemeData.trackVisibility test',
|
|
(WidgetTester tester) async {
|
|
final scrollController = ScrollController();
|
|
bool? getTrackVisibility(Set<WidgetState> states) {
|
|
return true;
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false).copyWith(
|
|
scrollbarTheme: _scrollbarTheme(
|
|
trackVisibility: WidgetStateProperty.resolveWith(getTrackVisibility),
|
|
),
|
|
),
|
|
home: ScrollConfiguration(
|
|
behavior: const NoScrollbarBehavior(),
|
|
child: Scrollbar(
|
|
thumbVisibility: true,
|
|
controller: scrollController,
|
|
child: SingleChildScrollView(
|
|
controller: scrollController,
|
|
child: const SizedBox(width: 4000.0, height: 4000.0),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
find.byType(Scrollbar),
|
|
paints
|
|
..rect(color: const Color(0x08000000))
|
|
..line(strokeWidth: 1.0, color: const Color(0x1a000000))
|
|
..rrect(color: const Color(0xff4caf50)),
|
|
);
|
|
|
|
scrollController.dispose();
|
|
},
|
|
variant: const TargetPlatformVariant(<TargetPlatform>{
|
|
TargetPlatform.linux,
|
|
TargetPlatform.macOS,
|
|
TargetPlatform.windows,
|
|
TargetPlatform.fuchsia,
|
|
}),
|
|
);
|
|
|
|
testWidgets('Default ScrollbarTheme debugFillProperties', (WidgetTester tester) async {
|
|
final builder = DiagnosticPropertiesBuilder();
|
|
const ScrollbarThemeData().debugFillProperties(builder);
|
|
|
|
final List<String> description = builder.properties
|
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
.map((DiagnosticsNode node) => node.toString())
|
|
.toList();
|
|
|
|
expect(description, <String>[]);
|
|
});
|
|
|
|
testWidgets('ScrollbarTheme implements debugFillProperties', (WidgetTester tester) async {
|
|
final builder = DiagnosticPropertiesBuilder();
|
|
ScrollbarThemeData(
|
|
thickness: WidgetStateProperty.resolveWith(_getThickness),
|
|
thumbVisibility: WidgetStateProperty.resolveWith(_getThumbVisibility),
|
|
radius: const Radius.circular(3.0),
|
|
thumbColor: WidgetStateProperty.resolveWith(_getThumbColor),
|
|
trackColor: WidgetStateProperty.resolveWith(_getTrackColor),
|
|
trackBorderColor: WidgetStateProperty.resolveWith(_getTrackBorderColor),
|
|
crossAxisMargin: 3.0,
|
|
mainAxisMargin: 6.0,
|
|
minThumbLength: 120.0,
|
|
).debugFillProperties(builder);
|
|
|
|
final List<String> description = builder.properties
|
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
.map((DiagnosticsNode node) => node.toString())
|
|
.toList();
|
|
|
|
expect(description, <String>[
|
|
"thumbVisibility: Instance of '_WidgetStatePropertyWith<bool?>'",
|
|
"thickness: Instance of '_WidgetStatePropertyWith<double?>'",
|
|
'radius: Radius.circular(3.0)',
|
|
"thumbColor: Instance of '_WidgetStatePropertyWith<Color?>'",
|
|
"trackColor: Instance of '_WidgetStatePropertyWith<Color?>'",
|
|
"trackBorderColor: Instance of '_WidgetStatePropertyWith<Color?>'",
|
|
'crossAxisMargin: 3.0',
|
|
'mainAxisMargin: 6.0',
|
|
'minThumbLength: 120.0',
|
|
]);
|
|
|
|
// On the web, Dart doubles and ints are backed by the same kind of object because
|
|
// JavaScript does not support integers. So, the Dart double "4.0" is identical
|
|
// to "4", which results in the web evaluating to the value "4" regardless of which
|
|
// one is used. This results in a difference for doubles in debugFillProperties between
|
|
// the web and the rest of Flutter's target platforms.
|
|
}, skip: kIsWeb); // [intended]
|
|
}
|
|
|
|
class NoScrollbarBehavior extends ScrollBehavior {
|
|
const NoScrollbarBehavior();
|
|
|
|
@override
|
|
Widget buildScrollbar(BuildContext context, Widget child, ScrollableDetails details) => child;
|
|
}
|
|
|
|
ScrollbarThemeData _scrollbarTheme({
|
|
WidgetStateProperty<double?>? thickness,
|
|
WidgetStateProperty<bool?>? trackVisibility,
|
|
WidgetStateProperty<bool?>? thumbVisibility,
|
|
Radius radius = const Radius.circular(6.0),
|
|
WidgetStateProperty<Color?>? thumbColor,
|
|
WidgetStateProperty<Color?>? trackColor,
|
|
WidgetStateProperty<Color?>? trackBorderColor,
|
|
double crossAxisMargin = 5.0,
|
|
double mainAxisMargin = 10.0,
|
|
double minThumbLength = 50.0,
|
|
}) {
|
|
return ScrollbarThemeData(
|
|
thickness: thickness ?? WidgetStateProperty.resolveWith(_getThickness),
|
|
trackVisibility:
|
|
trackVisibility ??
|
|
WidgetStateProperty.resolveWith(
|
|
(Set<WidgetState> states) => states.contains(WidgetState.hovered),
|
|
),
|
|
thumbVisibility: thumbVisibility,
|
|
radius: radius,
|
|
thumbColor: thumbColor ?? WidgetStateProperty.resolveWith(_getThumbColor),
|
|
trackColor: trackColor ?? WidgetStateProperty.resolveWith(_getTrackColor),
|
|
trackBorderColor: trackBorderColor ?? WidgetStateProperty.resolveWith(_getTrackBorderColor),
|
|
crossAxisMargin: crossAxisMargin,
|
|
mainAxisMargin: mainAxisMargin,
|
|
minThumbLength: minThumbLength,
|
|
);
|
|
}
|
|
|
|
double? _getThickness(Set<WidgetState> states) {
|
|
if (states.contains(WidgetState.hovered)) {
|
|
return 20.0;
|
|
}
|
|
return 10.0;
|
|
}
|
|
|
|
bool? _getThumbVisibility(Set<WidgetState> states) => true;
|
|
|
|
Color? _getThumbColor(Set<WidgetState> states) {
|
|
if (states.contains(WidgetState.dragged)) {
|
|
return Colors.red;
|
|
}
|
|
if (states.contains(WidgetState.hovered)) {
|
|
return Colors.blue;
|
|
}
|
|
return Colors.green;
|
|
}
|
|
|
|
Color? _getTrackColor(Set<WidgetState> states) {
|
|
if (states.contains(WidgetState.hovered)) {
|
|
return Colors.black;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Color? _getTrackBorderColor(Set<WidgetState> states) {
|
|
if (states.contains(WidgetState.hovered)) {
|
|
return Colors.yellow;
|
|
}
|
|
return null;
|
|
}
|