// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:math'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import '../common.dart'; Map<String, WidgetBuilder> gradientPerfRoutes = <String, WidgetBuilder>{ kGradientPerfRecreateDynamicRouteName: (BuildContext _) => const RecreateDynamicPainterPage(), kGradientPerfRecreateConsistentRouteName: (BuildContext _) => const RecreateConsistentPainterPage(), kGradientPerfStaticConsistentRouteName: (BuildContext _) => const StaticConsistentPainterPage(), }; typedef CustomPaintFactory = CustomPainter Function(double hue); class GradientPerfHomePage extends StatelessWidget { const GradientPerfHomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Gradient Perf')), body: ListView( key: const Key(kGradientPerfScrollableName), children: <Widget>[ ElevatedButton( key: const Key(kGradientPerfRecreateDynamicRouteName), child: const Text('Recreate Dynamic Gradients'), onPressed: () { Navigator.pushNamed(context, kGradientPerfRecreateDynamicRouteName); }, ), ElevatedButton( key: const Key(kGradientPerfRecreateConsistentRouteName), child: const Text('Recreate Same Gradients'), onPressed: () { Navigator.pushNamed(context, kGradientPerfRecreateConsistentRouteName); }, ), ElevatedButton( key: const Key(kGradientPerfStaticConsistentRouteName), child: const Text('Static Gradients'), onPressed: () { Navigator.pushNamed(context, kGradientPerfStaticConsistentRouteName); }, ), ], ), ); } } class _PainterPage extends StatefulWidget { const _PainterPage({super.key, required this.title, required this.factory}); final String title; final CustomPaintFactory factory; @override State<_PainterPage> createState() => _PainterPageState(); } class RecreateDynamicPainterPage extends _PainterPage { const RecreateDynamicPainterPage({super.key}) : super(title: 'Recreate Dynamic Gradients', factory: makePainter); static CustomPainter makePainter(double f) { return RecreatedDynamicGradients(baseFactor: f); } } class RecreateConsistentPainterPage extends _PainterPage { const RecreateConsistentPainterPage({super.key}) : super(title: 'Recreate Same Gradients', factory: makePainter); static CustomPainter makePainter(double f) { return RecreatedConsistentGradients(baseFactor: f); } } class StaticConsistentPainterPage extends _PainterPage { const StaticConsistentPainterPage({super.key}) : super(title: 'Reuse Same Gradients', factory: makePainter); static CustomPainter makePainter(double f) { return StaticConsistentGradients(baseFactor: f); } } class _PainterPageState extends State<_PainterPage> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController(vsync: this); _controller.repeat(period: const Duration(seconds: 2)); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: AnimatedBuilder( animation: _controller, builder: (BuildContext context, Widget? child) { return CustomPaint( size: const Size(paintW, paintH), painter: widget.factory(_controller.value), willChange: true, ); }, ), ), ); } } Color color(double factor) { int v = ((factor * 255 * 3) % (255 * 3)).round(); if (v < 0) { v += 255 * 3; } int r = 0; int g = 0; int b = 0; if (v < 255) { r = 255 - v; g = v; } else { v -= 255; if (v < 255) { g = 255 - v; b = v; } else { v -= 255; b = 255 - v; r = v; } } return Color.fromARGB(255, r, g, b); } Shader rotatingGradient(double factor, double x, double y, double h) { final double s = sin(factor * 2 * pi) * h/8; final double c = cos(factor * 2 * pi) * h/8; final double cx = x; final double cy = y + h/2; final Offset p0 = Offset(cx + s, cy + c); final Offset p1 = Offset(cx - s, cy - c); return ui.Gradient.linear(p0, p1, <Color>[ color(factor), color(factor + 0.5), ]); } const int nAcross = 12; const int nDown = 16; const double cellW = 20; const double cellH = 20; const double hGap = 5; const double vGap = 5; const double paintW = hGap + (cellW + hGap) * nAcross; const double paintH = vGap + (cellH + vGap) * nDown; double x(int i, int j) { return hGap + i * (cellW + hGap); } double y(int i, int j) { return vGap + j * (cellH + vGap); } Shader gradient(double baseFactor, int i, int j) { final double lineFactor = baseFactor + 1/3 + 0.5 * (j + 1) / (nDown + 1); final double cellFactor = lineFactor + 1/3 * (i + 1) / (nAcross + 1); return rotatingGradient(cellFactor, x(i, j) + cellW / 2, y(i, j), cellH); } class RecreatedDynamicGradients extends CustomPainter { RecreatedDynamicGradients({required this.baseFactor}); final double baseFactor; @override void paint(Canvas canvas, Size size) { final Paint p = Paint(); p.color = color(baseFactor); canvas.drawRect(Offset.zero & size, p); for (int j = 0; j < nDown; j++) { for (int i = 0; i < nAcross; i++) { p.shader = gradient(baseFactor, i, j); canvas.drawRect(Rect.fromLTWH(x(i, j), y(i, j), cellW, cellH), p); } } } @override bool shouldRepaint(CustomPainter oldDelegate) => true; } class RecreatedConsistentGradients extends CustomPainter { RecreatedConsistentGradients({required this.baseFactor}); final double baseFactor; @override void paint(Canvas canvas, Size size) { final Paint p = Paint(); p.color = color(baseFactor); canvas.drawRect(Offset.zero & size, p); for (int j = 0; j < nDown; j++) { for (int i = 0; i < nAcross; i++) { p.shader = gradient(0, i, j); canvas.drawRect(Rect.fromLTWH(x(i, j), y(i, j), cellW, cellH), p); } } } @override bool shouldRepaint(CustomPainter oldDelegate) => true; } class StaticConsistentGradients extends CustomPainter { StaticConsistentGradients({required this.baseFactor}); final double baseFactor; static List<List<Shader>> gradients = <List<Shader>>[ for (int j = 0; j < nDown; j++) <Shader>[ for (int i = 0; i < nAcross; i++) gradient(0, i, j), ], ]; @override void paint(Canvas canvas, Size size) { final Paint p = Paint(); p.color = color(baseFactor); canvas.drawRect(Offset.zero & size, p); for (int j = 0; j < nDown; j++) { for (int i = 0; i < nAcross; i++) { p.shader = gradients[j][i]; canvas.drawRect(Rect.fromLTWH(x(i, j), y(i, j), cellW, cellH), p); } } } @override bool shouldRepaint(CustomPainter oldDelegate) => true; }