Skip to content

Instantly share code, notes, and snippets.

@rodydavis
Last active December 23, 2022 23:18
Show Gist options
  • Save rodydavis/52f9c6ecc37a33cedf45225b5a4eb855 to your computer and use it in GitHub Desktop.
Save rodydavis/52f9c6ecc37a33cedf45225b5a4eb855 to your computer and use it in GitHub Desktop.
Flutter Canvas Demo
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