| // 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:async'; |
| |
| import 'package:flutter/material.dart'; |
| |
| import 'recorder.dart'; |
| |
| /// Creates a [Wrap] inside a ListView. |
| /// |
| /// Tests large number of DOM nodes since image breaks up large canvas. |
| class BenchWrapBoxScroll extends WidgetRecorder { |
| BenchWrapBoxScroll() : super(name: benchmarkName); |
| |
| static const String benchmarkName = 'bench_wrapbox_scroll'; |
| |
| @override |
| Widget createWidget() { |
| return MaterialApp( |
| theme: ThemeData( |
| primarySwatch: Colors.blue, |
| ), |
| title: 'WrapBox Scroll Benchmark', |
| home: const Scaffold(body: MyHomePage()), |
| ); |
| } |
| } |
| |
| class MyHomePage extends StatefulWidget { |
| const MyHomePage({super.key}); |
| |
| @override |
| State<MyHomePage> createState() => _MyHomePageState(); |
| } |
| |
| class _MyHomePageState extends State<MyHomePage> { |
| late ScrollController scrollController; |
| int block = 0; |
| static const Duration stepDuration = Duration(milliseconds: 500); |
| static const double stepDistance = 400; |
| |
| @override |
| void initState() { |
| super.initState(); |
| |
| scrollController = ScrollController(); |
| |
| // Without the timer the animation doesn't begin. |
| Timer.run(() async { |
| while (block < 25) { |
| await scrollController.animateTo((block % 5) * stepDistance, |
| duration: stepDuration, curve: Curves.easeInOut); |
| block++; |
| } |
| }); |
| } |
| |
| @override |
| void dispose() { |
| scrollController.dispose(); |
| super.dispose(); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return ListView( |
| controller: scrollController, |
| children: <Widget>[ |
| Wrap( |
| children: <Widget>[ |
| for (int i = 0; i < 30; i++) |
| FractionallySizedBox( |
| widthFactor: 0.2, |
| child: ProductPreview(i)), //need case1 |
| for (int i = 0; i < 30; i++) ProductPreview(i), //need case2 |
| ], |
| ), |
| ]); |
| } |
| } |
| |
| class ProductPreview extends StatelessWidget { |
| const ProductPreview(this.previewIndex, {super.key}); |
| |
| final int previewIndex; |
| |
| @override |
| Widget build(BuildContext context) { |
| return GestureDetector( |
| behavior: HitTestBehavior.translucent, |
| onTap: () => print('tap'), |
| child: Column( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| Container( |
| margin: const EdgeInsets.all(23), |
| padding: const EdgeInsets.all(18), |
| decoration: const BoxDecoration( |
| color: Color(0xfff9f9f9), |
| shape: BoxShape.circle, |
| ), |
| child: Image.network( |
| 'assets/assets/Icon-192.png', |
| width: 100, |
| height: 100, |
| ), |
| ), |
| const Text( |
| 'title', |
| ), |
| const SizedBox( |
| height: 14, |
| ), |
| Wrap( |
| alignment: WrapAlignment.center, |
| children: <Widget>[ |
| ProductOption( |
| optionText: '$previewIndex: option1', |
| ), |
| ProductOption( |
| optionText: '$previewIndex: option2', |
| ), |
| ProductOption( |
| optionText: '$previewIndex: option3', |
| ), |
| ProductOption( |
| optionText: '$previewIndex: option4', |
| ), |
| ProductOption( |
| optionText: '$previewIndex: option5', |
| ), |
| ], |
| ), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class ProductOption extends StatelessWidget { |
| const ProductOption({ |
| super.key, |
| required this.optionText, |
| }); |
| |
| final String optionText; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Container( |
| constraints: const BoxConstraints(minWidth: 56), |
| margin: const EdgeInsets.all(2), |
| padding: const EdgeInsets.symmetric(horizontal: 11, vertical: 5), |
| decoration: BoxDecoration( |
| border: Border.all( |
| color: const Color(0xffebebeb), |
| ), |
| borderRadius: const BorderRadius.all(Radius.circular(15)), |
| ), |
| child: Text( |
| optionText, |
| maxLines: 1, |
| textAlign: TextAlign.center, |
| overflow: TextOverflow.ellipsis, |
| ), |
| ); |
| } |
| } |