-
-
Save slightfoot/cac192587f99e84be894e58788e4664f to your computer and use it in GitHub Desktop.
Zooming and reorderable list :: https://www.youtube.com/watch?v=92TileEhN3g
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class AppState { | |
AppState(); | |
final zoomScale = ValueNotifier(1.0); // Initial zoom level | |
} | |
class MyApp extends StatefulWidget { | |
const MyApp({super.key}); | |
static AppState appStateOf(BuildContext context) { | |
return context.findAncestorStateOfType<_MyAppState>()!.state; | |
} | |
@override | |
State<MyApp> createState() => _MyAppState(); | |
} | |
class _MyAppState extends State<MyApp> { | |
final state = AppState(); | |
@override | |
Widget build(BuildContext context) { | |
return ZoomScaleChild( | |
child: MaterialApp( | |
home: const ZoomableReorderableList(), | |
), | |
); | |
} | |
} | |
class ZoomableReorderableList extends StatefulWidget { | |
const ZoomableReorderableList({super.key}); | |
@override | |
State<ZoomableReorderableList> createState() => _ZoomableReorderableListState(); | |
} | |
class _ZoomableReorderableListState extends State<ZoomableReorderableList> { | |
final List<String> _items = List.generate(10, (index) => 'Item $index'); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('Zoomable Reorderable List'), | |
actions: [ | |
IconButton( | |
icon: const Icon(Icons.zoom_in), | |
onPressed: () { | |
final appState = MyApp.appStateOf(context); | |
appState.zoomScale.value = // | |
(appState.zoomScale.value + 0.2).clamp( | |
0.5, | |
2.0, | |
); | |
}, | |
), | |
IconButton( | |
icon: const Icon(Icons.zoom_out), | |
onPressed: () { | |
final appState = MyApp.appStateOf(context); | |
appState.zoomScale.value = // | |
(appState.zoomScale.value - 0.2).clamp( | |
0.5, | |
2.0, | |
); | |
}, | |
), | |
], | |
), | |
body: SingleChildScrollView( | |
child: Center( | |
child: ReorderableListView( | |
shrinkWrap: true, | |
// Ensure the list fits within the scaled container | |
physics: const NeverScrollableScrollPhysics(), | |
// Disable inner scroll | |
onReorder: (oldIndex, newIndex) { | |
setState(() { | |
if (newIndex > oldIndex) { | |
newIndex -= 1; | |
} | |
final item = _items.removeAt(oldIndex); | |
_items.insert(newIndex, item); | |
}); | |
}, | |
children: _items.map((item) { | |
return Card( | |
key: ValueKey(item), // Unique key for each item | |
margin: const EdgeInsets.symmetric( | |
vertical: 4, | |
horizontal: 8, | |
), | |
child: InkWell( | |
onTap: () { | |
showDialog( | |
context: context, | |
builder: (_) => TestDialog(), | |
); | |
}, | |
child: Padding( | |
padding: const EdgeInsets.symmetric( | |
horizontal: 16.0, | |
vertical: 12.0, | |
), | |
child: Text(item), | |
), | |
), | |
); | |
}).toList(), | |
), | |
), | |
), | |
); | |
} | |
} | |
class ZoomScaleChild extends StatelessWidget { | |
const ZoomScaleChild({ | |
super.key, | |
required this.child, | |
}); | |
final Widget child; | |
@override | |
Widget build(BuildContext context) { | |
return ZoomScaleBuilder( | |
builder: (BuildContext context, double zoomScale, Widget? child) { | |
return MediaQuery( | |
data: | |
MediaQuery.of(context) // | |
.copyWith(textScaler: TextScaler.linear(zoomScale)), | |
child: child!, | |
); | |
// final size = MediaQuery.sizeOf(context); | |
// return Align( | |
// alignment: Alignment.topCenter, | |
// child: Container( | |
// constraints: BoxConstraints.tightFor( | |
// width: size.width / zoomScale, | |
// height: size.height / zoomScale, | |
// ), | |
// child: Transform.scale( | |
// scale: zoomScale, | |
// alignment: Alignment.topCenter, | |
// child: child, | |
// ), | |
// ), | |
// ); | |
}, | |
child: child, | |
); | |
} | |
} | |
class ZoomScaleBuilder extends StatelessWidget { | |
const ZoomScaleBuilder({ | |
super.key, | |
required this.builder, | |
this.child, | |
}); | |
final Widget Function(BuildContext context, double zoomScale, Widget? child) builder; | |
final Widget? child; | |
@override | |
Widget build(BuildContext context) { | |
return ValueListenableBuilder( | |
valueListenable: MyApp.appStateOf(context).zoomScale, | |
builder: builder, | |
child: child, | |
); | |
} | |
} | |
class TestDialog extends StatelessWidget { | |
const TestDialog({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return AlertDialog( | |
content: Text('hello'), | |
actions: [ | |
TextButton( | |
onPressed: () => Navigator.of(context).pop(), | |
child: Text('OK'), | |
), | |
], | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks.