render_proof 0.1.2
render_proof: ^0.1.2 copied to clipboard
A comprehensive set of tests to validate your custom render objects.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:render_proof/example_render_objects.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Render Proof Gallery',
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.dark,
scaffoldBackgroundColor: const Color(0xFF0A0E14),
colorScheme: const ColorScheme.dark(
primary: Color(0xFF00FFFF),
secondary: Color(0xFFFF00FF),
surface: Color(0xFF161B22),
),
textTheme: const TextTheme(
displayLarge: TextStyle(color: Color(0xFF00FFFF), fontWeight: FontWeight.bold, letterSpacing: 2),
titleMedium: TextStyle(color: Color(0xFFFF00FF), fontWeight: FontWeight.w600),
),
useMaterial3: true,
),
home: const GalleryHome(),
);
}
}
class DemoItem {
final String name;
final String description;
final IconData icon;
final WidgetBuilder builder;
const DemoItem({
required this.name,
required this.description,
required this.icon,
required this.builder,
});
}
class GalleryHome extends StatefulWidget {
const GalleryHome({super.key});
@override
State<GalleryHome> createState() => _GalleryHomeState();
}
class _GalleryHomeState extends State<GalleryHome> {
int _selectedDemoIndex = 0;
final List<DemoItem> _demos = [
DemoItem(
name: 'Custom Painter',
icon: Icons.brush_outlined,
description: 'A leaf render object that draws a multi-color gradient using raw vertices. This bypasses the standard canvas draw calls for higher performance complex geometries.',
builder: (context) => const Center(
child: SizedBox(
width: 300,
height: 300,
child: BasicCustomPainter(
topLeftColor: Colors.cyan,
topRightColor: Color(0xFFFF00FF),
bottomLeftColor: Colors.yellow,
bottomRightColor: Colors.blue,
),
),
),
),
DemoItem(
name: 'Proxy Layout',
icon: Icons.rotate_right_outlined,
description: 'A single-child render object that rotates its child by 45 degrees. It demonstrates how to apply paint transforms and correctly handle hit testing for transformed children.',
builder: (context) => Center(
child: BasicProxy(
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: const Color(0xFFFF00FF).withOpacity(0.8),
border: Border.all(color: Colors.cyan, width: 2),
boxShadow: [
BoxShadow(color: const Color(0xFFFF00FF).withOpacity(0.5), blurRadius: 20, spreadRadius: 5),
],
),
child: const Center(
child: Text(
'ROTATED',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 24),
),
),
),
),
),
),
DemoItem(
name: 'Slotted Layout',
icon: Icons.view_quilt_outlined,
description: 'A multi-child layout with specific named slots: left, content, and right. It supports interactive resizing of the side panes and demonstrates SlottedMultiChildRenderObjectWidget.',
builder: (context) => DesktopPaneLayout(
leftPane: Container(
margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.cyan.withOpacity(0.1),
border: Border.all(color: Colors.cyan.withOpacity(0.5)),
),
child: const Center(child: Text('Left Slot', style: TextStyle(color: Colors.cyan))),
),
contentPane: Container(
margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFFFF00FF).withOpacity(0.1),
border: Border.all(color: const Color(0xFFFF00FF).withOpacity(0.5)),
),
child: const Center(child: Text('Content Slot', style: TextStyle(color: Color(0xFFFF00FF)))),
),
rightPane: Container(
margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.yellow.withOpacity(0.1),
border: Border.all(color: Colors.yellow.withOpacity(0.5)),
),
child: const Center(child: Text('Right Slot', style: TextStyle(color: Colors.yellow))),
),
),
),
DemoItem(
name: 'Collection Layout',
icon: Icons.bubble_chart_outlined,
description: 'A multi-child layout that uses a force-directed physics algorithm to pack children tightly without overlap. Ideal for clusters of related items.',
builder: (context) => BubbleCloud(
children: List.generate(25, (index) {
final color = [Colors.cyan, const Color(0xFFFF00FF), Colors.yellow, Colors.greenAccent][index % 4];
final size = 40.0 + (index % 7) * 8.0;
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: color.withOpacity(0.2),
shape: BoxShape.circle,
border: Border.all(color: color, width: 2),
boxShadow: [
BoxShadow(color: color.withOpacity(0.3), blurRadius: 10, spreadRadius: 2),
],
),
child: Center(
child: Text(
'$index',
style: TextStyle(color: color, fontWeight: FontWeight.bold, fontSize: 12),
),
),
);
}),
),
),
];
@override
Widget build(BuildContext context) {
final bool isDesktop = MediaQuery.of(context).size.width > 900;
final selectedDemo = _demos[_selectedDemoIndex];
return Scaffold(
appBar: isDesktop
? null
: AppBar(
title: const Text('RENDER PROOF', style: TextStyle(letterSpacing: 4, fontWeight: FontWeight.bold)),
backgroundColor: Colors.transparent,
elevation: 0,
centerTitle: true,
),
drawer: isDesktop ? null : _buildDrawer(),
body: isDesktop
? Column(
children: [
_buildDesktopAppBar(),
Expanded(
child: DesktopPaneLayout(
leftPane: _buildMenu(isDesktop: true),
contentPane: _buildContent(selectedDemo),
rightPane: _buildDetails(selectedDemo),
),
),
],
)
: _buildContent(selectedDemo),
);
}
Widget _buildDesktopAppBar() {
return Container(
height: 52,
decoration: const BoxDecoration(
color: Color(0xFF0D1117),
border: Border(bottom: BorderSide(color: Color(0xFF161B22), width: 1)),
),
child: Row(
children: [
// Padding for macOS traffic lights
const SizedBox(width: 80),
const Icon(Icons.token, size: 18, color: Colors.cyan),
const SizedBox(width: 12),
const Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'RENDER PROOF',
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold,
letterSpacing: 1.2,
),
),
Text(
'GALLERY_VIEW_ACTIVE',
style: TextStyle(
color: Colors.white24,
fontSize: 9,
fontWeight: FontWeight.w500,
letterSpacing: 1,
),
),
],
),
const Spacer(),
IconButton(
onPressed: () {},
icon: const Icon(Icons.search, size: 20),
color: Colors.white38,
),
const SizedBox(width: 8),
IconButton(
onPressed: () {},
icon: const Icon(Icons.settings_outlined, size: 20),
color: Colors.white38,
),
const SizedBox(width: 16),
],
),
);
}
Widget _buildDrawer() {
return Drawer(
backgroundColor: const Color(0xFF0A0E14),
child: Column(
children: [
const DrawerHeader(
child: Center(
child: Text(
'RENDER\nPROOF',
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF00FFFF),
fontSize: 32,
fontWeight: FontWeight.bold,
letterSpacing: 8,
),
),
),
),
Expanded(child: _buildMenu(isDesktop: false)),
],
),
);
}
Widget _buildMenu({required bool isDesktop}) {
return Container(
decoration: BoxDecoration(
color: isDesktop ? const Color(0xFF0D1117) : null,
border: isDesktop ? const Border(right: BorderSide(color: Color(0xFF161B22), width: 1)) : null,
),
child: Row(
children: [
if (isDesktop) ...[
// Obsidian-style Ribbon
Container(
width: 48,
decoration: const BoxDecoration(
border: Border(right: BorderSide(color: Color(0xFF161B22), width: 1)),
),
child: Column(
children: [
const SizedBox(height: 12),
_buildRibbonIcon(Icons.folder_outlined, true),
_buildRibbonIcon(Icons.search, false),
_buildRibbonIcon(Icons.hub_outlined, false),
_buildRibbonIcon(Icons.inventory_2_outlined, false),
const Spacer(),
_buildRibbonIcon(Icons.settings_outlined, false),
const SizedBox(height: 12),
],
),
),
],
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isDesktop)
const Padding(
padding: EdgeInsets.only(left: 16, top: 20, bottom: 8),
child: Text(
'RENDER_PROOF',
style: TextStyle(
color: Colors.white24,
fontSize: 9,
fontWeight: FontWeight.bold,
letterSpacing: 1.2,
),
),
),
Expanded(
child: ListView.builder(
itemCount: _demos.length,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
itemBuilder: (context, index) {
final isSelected = _selectedDemoIndex == index;
final demo = _demos[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 1),
child: ListTile(
selected: isSelected,
dense: true,
visualDensity: const VisualDensity(vertical: -4, horizontal: -4),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
selectedTileColor: Colors.white.withOpacity(0.05),
leading: Icon(
demo.icon,
size: 16,
color: isSelected ? Colors.cyan : Colors.white30,
),
title: Text(
demo.name,
style: TextStyle(
color: isSelected ? Colors.white : Colors.white60,
fontSize: 12,
fontWeight: isSelected ? FontWeight.w500 : FontWeight.normal,
),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 12),
onTap: () {
setState(() {
_selectedDemoIndex = index;
});
if (!isDesktop) {
Navigator.pop(context);
}
},
),
);
},
),
),
],
),
),
],
),
);
}
Widget _buildRibbonIcon(IconData icon, bool isActive) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Icon(
icon,
size: 20,
color: isActive ? Colors.white70 : Colors.white24,
),
);
}
Widget _buildContent(DemoItem demo) {
return Container(
color: Colors.black12,
child: Stack(
children: [
Positioned.fill(
child: CustomPaint(
painter: GridPainter(),
),
),
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: KeyedSubtree(
key: ValueKey(demo.name),
child: demo.builder(context),
),
),
],
),
);
}
Widget _buildDetails(DemoItem demo) {
return Container(
padding: const EdgeInsets.all(32),
decoration: const BoxDecoration(
border: Border(left: BorderSide(color: Color(0xFF161B22), width: 1)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'DEMO_INFO',
style: TextStyle(
color: const Color(0xFFFF00FF).withOpacity(0.5),
fontWeight: FontWeight.bold,
letterSpacing: 4,
fontSize: 12,
),
),
const SizedBox(height: 8),
Text(
demo.name.toUpperCase(),
style: const TextStyle(
color: Colors.cyan,
fontWeight: FontWeight.bold,
letterSpacing: 2,
fontSize: 28,
),
),
const SizedBox(height: 16),
const Divider(color: Colors.cyan, thickness: 1),
const SizedBox(height: 24),
Text(
demo.description,
style: const TextStyle(fontSize: 16, color: Colors.white70, height: 1.6),
),
const Spacer(),
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.cyan.withOpacity(0.05),
border: Border.all(color: Colors.cyan.withOpacity(0.3)),
borderRadius: BorderRadius.circular(4),
),
child: Row(
children: [
const Icon(Icons.bolt, color: Colors.cyan),
const SizedBox(width: 16),
Expanded(
child: Text(
'BUILD_STABLE: 1.0.0-PROX\nSYSTEM_STATUS: ACTIVE',
style: TextStyle(
color: Colors.cyan.withOpacity(0.8),
fontSize: 10,
fontFamily: 'monospace',
),
),
),
],
),
),
],
),
);
}
}
class GridPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.cyan.withOpacity(0.05)
..strokeWidth = 1;
const spacing = 40.0;
for (double i = 0; i < size.width; i += spacing) {
canvas.drawLine(Offset(i, 0), Offset(i, size.height), paint);
}
for (double i = 0; i < size.height; i += spacing) {
canvas.drawLine(Offset(0, i), Offset(size.width, i), paint);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}