Last active
December 23, 2022 23:18
-
-
Save rodydavis/52f9c6ecc37a33cedf45225b5a4eb855 to your computer and use it in GitHub Desktop.
Flutter Canvas Demo
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 StoryBoardExample()); | |
} | |
class StoryBoardExample extends StatelessWidget { | |
const StoryBoardExample({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
debugShowCheckedModeBanner: false, | |
home: StoryBoard( | |
screens: [ | |
StoryBoardScreen( | |
size: const Size(100, 100), | |
offset: const Offset(0, 0), | |
child: Container( | |
color: Colors.red, | |
), | |
), | |
StoryBoardScreen( | |
size: const Size(100, 100), | |
offset: const Offset(100, 50), | |
child: Container( | |
color: Colors.green, | |
), | |
), | |
StoryBoardScreen( | |
size: const Size(100, 100), | |
offset: const Offset(200, 100), | |
child: Container( | |
color: Colors.yellow, | |
), | |
), | |
StoryBoardScreen( | |
size: const Size(100, 100), | |
offset: const Offset(300, 150), | |
child: Container( | |
color: Colors.purple, | |
), | |
), | |
StoryBoardScreen( | |
size: const Size(100, 100), | |
offset: const Offset(400, 200), | |
child: Container( | |
color: Colors.pink, | |
), | |
), | |
], | |
), | |
); | |
} | |
} | |
class StoryBoardScreen { | |
StoryBoardScreen({ | |
required this.child, | |
required this.size, | |
required this.offset, | |
}); | |
final Widget child; | |
Offset offset; | |
Size size; | |
} | |
class StoryBoard extends StatefulWidget { | |
const StoryBoard({ | |
super.key, | |
required this.screens, | |
}); | |
final List<StoryBoardScreen> screens; | |
@override | |
State<StoryBoard> createState() => _StoryBoardState(); | |
} | |
class _StoryBoardState extends State<StoryBoard> { | |
late List<StoryBoardScreen> screens = widget.screens; | |
List<StoryBoardScreen> selectedScreens = []; | |
@override | |
Widget build(BuildContext context) { | |
return Material( | |
child: InteractiveViewer( | |
child: Stack( | |
children: [ | |
const Positioned.fill( | |
child: GridPaper(), | |
), | |
Positioned.fill( | |
child: CustomMultiChildLayout( | |
delegate: StoryBoardDelegate(screens), | |
children: screens.map(buildScreen).toList(), | |
), | |
), | |
], | |
), | |
), | |
); | |
} | |
Widget buildScreen(StoryBoardScreen screen) { | |
return LayoutId( | |
id: screen, | |
child: GestureDetector( | |
onPanUpdate: (details) { | |
if (mounted && selectedScreens.contains(screen)) { | |
setState(() { | |
screen.offset += details.delta; | |
}); | |
} | |
}, | |
onPanEnd: (details) { | |
if (mounted) { | |
setState(() { | |
selectedScreens = []; | |
}); | |
} | |
}, | |
onPanCancel: () { | |
if (mounted) { | |
setState(() { | |
selectedScreens = []; | |
}); | |
} | |
}, | |
onTapDown: (details) { | |
if (mounted) { | |
setState(() { | |
selectedScreens = [screen]; | |
}); | |
} | |
}, | |
onTapUp: (details) { | |
if (mounted) { | |
setState(() { | |
selectedScreens = []; | |
}); | |
} | |
}, | |
child: Container( | |
foregroundDecoration: BoxDecoration( | |
border: Border.all( | |
color: selectedScreens.contains(screen) | |
? Colors.blue | |
: Colors.transparent, | |
width: 2, | |
), | |
), | |
child: screen.child, | |
), | |
), | |
); | |
} | |
} | |
class StoryBoardDelegate extends MultiChildLayoutDelegate { | |
StoryBoardDelegate(this.screens); | |
final List<StoryBoardScreen> screens; | |
@override | |
void performLayout(Size size) { | |
for (final screen in screens) { | |
layoutChild(screen, BoxConstraints.tight(screen.size)); | |
positionChild(screen, screen.offset); | |
} | |
} | |
@override | |
bool shouldRelayout(StoryBoardDelegate oldDelegate) => true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment