render_proof 0.1.2 copy "render_proof: ^0.1.2" to clipboard
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;
}
1
likes
160
points
240
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A comprehensive set of tests to validate your custom render objects.

Homepage
Repository (GitHub)

License

MIT (license)

Dependencies

flutter, flutter_test

More

Packages that depend on render_proof