Skip to content

Instantly share code, notes, and snippets.

@Piinks
Created October 17, 2023 19:50
Show Gist options
  • Save Piinks/1d6cbd0ac83ac401628f0fc82b37afd2 to your computer and use it in GitHub Desktop.
Save Piinks/1d6cbd0ac83ac401628f0fc82b37afd2 to your computer and use it in GitHub Desktop.
Repro Customer Issue
import 'package:flutter/material.dart';
void main() {
runApp(const BugExample());
}
class BugExample extends StatelessWidget {
const BugExample();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.grey,
body: KeyboardAwareDraggableScrollableSheet(
builder: (_, controller) => SingleChildScrollView(
controller: controller,
child: Container(
color: Colors.white,
child: Column(
children: [
TextField(),
Text('one'),
Text('two'),
Text('three'),
Container(width: 50, height: 50, color: Colors.red),
Text('four'),
Text('five'),
Text('six'),
Container(width: 50, height: 50, color: Colors.green),
Text('seven'),
Text('eight'),
Text('nine'),
Container(width: 50, height: 50, color: Colors.blue),
Text('ten'),
Text('eleven'),
Text('twelve'),
],
),
),
),
),
),
);
}
}
class KeyboardAwareDraggableScrollableSheet extends StatefulWidget {
const KeyboardAwareDraggableScrollableSheet({
super.key,
required this.builder,
});
final ScrollableWidgetBuilder builder;
@override
State<KeyboardAwareDraggableScrollableSheet> createState() =>
_KeyboardAwareDraggableScrollableSheetState();
}
class _KeyboardAwareDraggableScrollableSheetState
extends State<KeyboardAwareDraggableScrollableSheet> {
var _virtualKeyboardUp = false;
var _previousPosition = 1.0;
late DraggableScrollableController _draggableScrollableController;
late ScrollController _draggableSheetScrollController;
@override
void initState() {
super.initState();
_draggableScrollableController = DraggableScrollableController();
}
void _updateLayoutForVirtualKeyboard(
BuildContext context,
BoxConstraints constraints,
) {
final bottom = MediaQuery.of(context).viewInsets.bottom;
final keyboardCurrentlyUp = bottom > 0;
if (_virtualKeyboardUp != keyboardCurrentlyUp) {
_virtualKeyboardUp = keyboardCurrentlyUp;
WidgetsBinding.instance.addPostFrameCallback((_) {
late double targetSize;
final sheetSize = _draggableScrollableController.isAttached
? _draggableScrollableController.size
: 0.5;
final scrollPosition = _draggableScrollableController.isAttached
? _draggableSheetScrollController.position.pixels
: 0;
if (_virtualKeyboardUp) {
targetSize = 1.0;
_previousPosition = sheetSize;
} else if (scrollPosition == 0) {
targetSize = _previousPosition;
} else {
return;
}
if (_draggableScrollableController.isAttached) {
_draggableScrollableController.animateTo(
targetSize,
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut,
);
}
});
}
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
_updateLayoutForVirtualKeyboard(context, constraints);
return DraggableScrollableSheet(
controller: _draggableScrollableController,
builder: (context, controller) {
_draggableSheetScrollController = controller;
final child = widget.builder(context, controller);
return Padding(
padding: EdgeInsetsDirectional.only(
top: MediaQuery.of(context).viewInsets.top,
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: child,
);
},
);
},
);
}
}
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:triage_october/main.dart';
void main() {
final keyboardHeight = ValueNotifier<double>(0);
Future<void> simulateKeyboardUp(WidgetTester tester) async {
keyboardHeight.value = 150;
await tester.pumpAndSettle();
}
Future<void> simulateKeyboardDown(WidgetTester tester) async {
await tester.pumpAndSettle();
}
testWidgets('Repro', (WidgetTester tester) async {
tester.view
..physicalSize = const Size(300, 400)
..devicePixelRatio = 1.0;
final app = MaterialApp(
home: Scaffold(
body: VirtualKeyboardSimulator(
keyboardHeight: keyboardHeight,
child: Container(
alignment: Alignment.bottomRight,
child: Stack(
children: [
Container(color: Colors.grey),
KeyboardAwareDraggableScrollableSheet(
builder: (_, controller) => SingleChildScrollView(
controller: controller,
child: Container(
color: Colors.white,
child: Column(
children: [
const Text('one'),
const Text('two'),
const Text('three'),
Container(width: 50, height: 50, color: Colors.red),
const Text('four'),
const Text('five'),
const Text('six'),
Container(width: 50, height: 50, color: Colors.green),
const Text('seven'),
const Text('eight'),
const Text('nine'),
Container(width: 50, height: 50, color: Colors.blue),
const Text('ten'),
const Text('eleven'),
const Text('twelve'),
],
),
),
),
),
],
),
),
),
),
);
await tester.pumpWidget(app);
await expectLater(
find.byType(MaterialApp),
matchesGoldenFile('1_bottom_sheet_default_state.png'),
);
await simulateKeyboardUp(tester);
await expectLater(
find.byType(MaterialApp),
matchesGoldenFile(
'2_bottom_sheet_with_keyboard_up.png',
),
);
// Scroll down to reveal the element 'seven'.
await tester.scrollUntilVisible(find.text('seven'), 5.0);
await tester.pumpAndSettle();
await expectLater(
find.byType(MaterialApp),
matchesGoldenFile(
'3_bottom_sheet_scrolled_down_with_keyboard_up.png',
),
);
await simulateKeyboardDown(tester);
// Because the user scrolled down, the sheet will stay up when the
// keyboard goes down.
await expectLater(
find.byType(MaterialApp),
matchesGoldenFile('4_bottom_sheet_scrolled_down.png'),
);
});
}
class VirtualKeyboardSimulator extends StatelessWidget {
const VirtualKeyboardSimulator({
super.key,
required this.keyboardHeight,
required this.child,
});
final Widget child;
final ValueNotifier<double> keyboardHeight;
Widget _keyboard() {
final rows = ['qwertyuiop', 'asdfghjkl', 'zxcvbnm'];
return Column(
children: [
const Text('Virtual Keyboard'),
for (String row in rows)
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (var j = 0; j < row.length; j++)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 2.0,
vertical: 3.0,
),
child: Container(
width: 21,
height: 21,
color: Colors.grey[200],
child: Center(
child: Text(
row.substring(j, j + 1),
),
),
),
),
],
),
),
],
);
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<double>(
valueListenable: keyboardHeight,
builder: (context, value, _) => MediaQuery(
data: MediaQueryData(
viewInsets: EdgeInsets.only(bottom: value),
),
child: Stack(
children: [
child,
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.grey[100],
height: value,
child: ClipRect(
child: OverflowBox(
maxHeight: double.infinity,
maxWidth: double.infinity,
alignment: Alignment.topCenter,
child: _keyboard(),
),
),
),
),
],
),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment