blob: 8e3a41ab92e40d3f500c521e4b950b5fa32bfa9a [file] [log] [blame]
// 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:typed_data';
import 'dart:ui';
import 'package:flutter/material.dart';
// Adapted from test case submitted in
// https://github.com/flutter/flutter/issues/92366
// Converted to use fixed data rather than reading a waveform file
class VeryLongPictureScrollingPerf extends StatefulWidget {
const VeryLongPictureScrollingPerf({super.key});
@override
State createState() => VeryLongPictureScrollingPerfState();
}
class VeryLongPictureScrollingPerfState extends State<VeryLongPictureScrollingPerf> {
bool consolidate = false;
bool useList = false;
Int16List waveData = loadGraph();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
Row(
children: <Widget>[
const Text('list:'),
Checkbox(value: useList, onChanged: (bool? value) => setState(() {
useList = value!;
}),),
],
),
Row(
children: <Widget>[
const Text('consolidate:'),
Checkbox(value: consolidate, onChanged: (bool? value) => setState(() {
consolidate = value!;
}),),
],
),
],
),
backgroundColor: Colors.transparent,
body: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: useList
? ListView.builder(
key: const ValueKey<String>('vlp_list_view_scrollable'),
scrollDirection: Axis.horizontal,
clipBehavior: Clip.none,
itemCount: (waveData.length / 200).ceil(),
itemExtent: 100,
itemBuilder: (BuildContext context, int index) => CustomPaint(
painter: PaintSomeTest(
waveData: waveData,
from: index * 200,
to: min((index + 1) * 200, waveData.length - 1),
)
),
)
: SingleChildScrollView(
key: const ValueKey<String>('vlp_single_child_scrollable'),
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width * 20,
height: MediaQuery.of(context).size.height,
child: RepaintBoundary(
child: CustomPaint(
isComplex: true,
painter: PaintTest(
consolidate: consolidate,
waveData: waveData,
),
),
),
),
),
),
);
}
}
class PaintTest extends CustomPainter {
const PaintTest({
required this.consolidate,
required this.waveData,
});
final bool consolidate;
final Int16List waveData;
@override
void paint(Canvas canvas, Size size) {
final double halfHeight = size.height / 2;
double x = 0;
const double strokeSize = .5;
const double zoomFactor = .5;
final Paint paintPos = Paint()
..color = Colors.pink
..strokeWidth = strokeSize
..isAntiAlias = false
..style = PaintingStyle.stroke;
final Paint paintNeg = Paint()
..color = Colors.pink
..strokeWidth = strokeSize
..isAntiAlias = false
..style = PaintingStyle.stroke;
final Paint paintZero = Paint()
..color = Colors.green
..strokeWidth = strokeSize
..isAntiAlias = false
..style = PaintingStyle.stroke;
int index = 0;
Paint? listPaint;
final Float32List offsets = Float32List(consolidate ? waveData.length * 4 : 4);
int used = 0;
for (index = 0; index < waveData.length; index++) {
final (Paint curPaint, Offset p1) = switch (waveData[index]) {
< 0 => (paintPos, Offset(x, halfHeight * (1 - waveData[index] / 32768))),
> 0 => (paintNeg, Offset(x, halfHeight * (1 - waveData[index] / 32767))),
_ => (paintZero, Offset(x, halfHeight + 1)),
};
final Offset p0 = Offset(x, halfHeight);
if (consolidate) {
if (listPaint != null && listPaint != curPaint) {
canvas.drawRawPoints(PointMode.lines, offsets.sublist(0, used), listPaint);
used = 0;
}
listPaint = curPaint;
offsets[used++] = p0.dx;
offsets[used++] = p0.dy;
offsets[used++] = p1.dx;
offsets[used++] = p1.dy;
} else {
canvas.drawLine(p0, p1, curPaint);
}
x += zoomFactor;
}
if (consolidate && used > 0) {
canvas.drawRawPoints(PointMode.lines, offsets.sublist(0, used), listPaint!);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return oldDelegate is! PaintTest ||
oldDelegate.consolidate != consolidate ||
oldDelegate.waveData != waveData;
}
}
class PaintSomeTest extends CustomPainter {
const PaintSomeTest({
required this.waveData,
int? from,
int? to,
}) : from = from ?? 0, to = to?? waveData.length;
final Int16List waveData;
final int from;
final int to;
@override
void paint(Canvas canvas, Size size) {
final double halfHeight = size.height / 2;
double x = 0;
const double strokeSize = .5;
const double zoomFactor = .5;
final Paint paintPos = Paint()
..color = Colors.pink
..strokeWidth = strokeSize
..isAntiAlias = false
..style = PaintingStyle.stroke;
final Paint paintNeg = Paint()
..color = Colors.pink
..strokeWidth = strokeSize
..isAntiAlias = false
..style = PaintingStyle.stroke;
final Paint paintZero = Paint()
..color = Colors.green
..strokeWidth = strokeSize
..isAntiAlias = false
..style = PaintingStyle.stroke;
for (int index = from; index <= to; index++) {
final (Paint curPaint, Offset p1) = switch (waveData[index]) {
< 0 => (paintPos, Offset(x, halfHeight * (1 - waveData[index] / 32768))),
> 0 => (paintNeg, Offset(x, halfHeight * (1 - waveData[index] / 32767))),
_ => (paintZero, Offset(x, halfHeight + 1)),
};
final Offset p0 = Offset(x, halfHeight);
canvas.drawLine(p0, p1, curPaint);
x += zoomFactor;
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return oldDelegate is! PaintSomeTest ||
oldDelegate.waveData != waveData ||
oldDelegate.from != from ||
oldDelegate.to != to;
}
}
Int16List loadGraph() {
final Int16List waveData = Int16List(350000);
final Random r = Random(0x42);
for (int i = 0; i < waveData.length; i++) {
waveData[i] = r.nextInt(32768) - 16384;
}
return waveData;
}