Skip to content

Performance drops on Android on simple widgets #169492

@RocksteadyDog

Description

@RocksteadyDog

Steps to reproduce

Hello!

  1. Run the example on android (my phone is pixel 6)
  2. Look at the performance graphs:
    raster thread ~33 ms, looks scary)
    ui thread ~ 6.7 ms, that's not normal right?
  3. Run on macbook, everything is fine

Expected results

Good performance

Actual results

Bad performance

Code sample

Code sample
import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      showPerformanceOverlay: true,
      debugShowCheckedModeBanner: false,
      scrollBehavior: CustomScrollBehavior(),
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const CategoriesPage(),
    );
  }
}

class CategoriesPage extends StatelessWidget {
  const CategoriesPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'Categories',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        centerTitle: false,
      ),
      body: Padding(
        padding: const EdgeInsets.only(top: 16),
        child: ListView.builder(
          itemCount: categories.length,
          itemBuilder: (context, index) {
            return Padding(
              padding: EdgeInsets.only(bottom: 16),
              child: CategoryTile(data: categories[index]),
            );
          },
        ),
      ),
    );
  }
}

class CategoryTile extends StatelessWidget {
  const CategoryTile({super.key, required this.data});

  final CategoryData data;

  @override
  Widget build(BuildContext context) {
    var textTheme = Theme.of(context).textTheme;
    var colorScheme = Theme.of(context).colorScheme;
    var size = (MediaQuery.of(context).size.width - 12) / 3;
    size = size * 2;

    return Column(
      children: [
        Padding(
          padding: const EdgeInsets.only(left: 16.0, right: 32),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                data.categoryName,
                style: textTheme.titleLarge?.copyWith(
                  fontWeight: FontWeight.bold,
                ),
              ),
              Text(
                'All',
                style: textTheme.titleMedium?.copyWith(
                  color: colorScheme.primary,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        ),
        SizedBox(height: 8),

        SizedBox(
          height: size,
          child: GridView.builder(
            padding: EdgeInsets.only(left: 16),
            scrollDirection: Axis.horizontal,
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              crossAxisSpacing: 12.0,
              mainAxisSpacing: 12.0,
              childAspectRatio: 1.0,
            ),
            itemCount: data.puzzles.length,
            itemBuilder: (context, index) {
              return TestCard(data: data.puzzles[index]);
            },
          ),
        ),
      ],
    );
  }
}

class TestCard extends StatefulWidget {
  const TestCard({super.key, required this.data});

  final PictureData data;

  @override
  State<TestCard> createState() => _TestCardState();
}

class _TestCardState extends State<TestCard> {
  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        image: DecorationImage(
          image: AssetImage("assets/white_rect.png"),
          centerSlice: Rect.fromLTWH(16, 16, 16, 16),
          fit: BoxFit.contain,
          alignment: Alignment.center,
          scale: 3,
          filterQuality: FilterQuality.none,
          colorFilter: ColorFilter.mode(
            cardSurfaceColor(context),
            BlendMode.modulate,
          ),
        ),
      ),
      child: Stack(
        fit: StackFit.expand,
        children: [
          FractionallySizedBox(
            widthFactor: 0.7,
            heightFactor: 0.7,
            child: Image.asset(
              widget.data.pictureUrl,
              fit: BoxFit.cover,
              filterQuality: FilterQuality.none,
            ),
          ),
        ],
      ),
    );
  }
}

Color cardSurfaceColor(BuildContext context) {
  var colorScheme = Theme.of(context).colorScheme;
  return ElevationOverlay.applySurfaceTint(
    colorScheme.surfaceContainerLow,
    colorScheme.surfaceTint,
    0,
  );
}

class PictureData {
  final String pictureUrl;

  PictureData({required this.pictureUrl});
}

class CategoryData {
  final String categoryName;
  final List<PictureData> puzzles;

  CategoryData({required this.categoryName, required this.puzzles});
}

List<CategoryData> categories = [
  CategoryData(categoryName: "Books", puzzles: defaults),
  CategoryData(categoryName: "Animals", puzzles: defaults),
  CategoryData(categoryName: "Home", puzzles: defaults),
  CategoryData(categoryName: "Nature", puzzles: defaults),
  CategoryData(categoryName: "World", puzzles: defaults),
];

List<PictureData> defaults = List.generate(32, (index) {
  final urls = [
    'assets/images/kitty.jpeg',
    'assets/images/banana.jpg',
    'assets/images/food.jpg',
  ];
  String url = urls[index % urls.length];

  var data = PictureData(pictureUrl: url);
  return data;
});

class CustomScrollBehavior extends MaterialScrollBehavior {
  @override
  Set<PointerDeviceKind> get dragDevices => {
    PointerDeviceKind.touch,
    PointerDeviceKind.mouse,
    PointerDeviceKind.trackpad,
    PointerDeviceKind.stylus,
    PointerDeviceKind.unknown,
  };
}

Screenshots or Video

Screenshots / Video demonstration

Android:
https://drive.google.com/file/d/1y68R3FnFHCFCbcEJpI4z2E9nKVtmm3ha/view?usp=sharing
Mac:
https://drive.google.com/file/d/1zBBYyxIXhiZhb6jaiGYFY2-qW4hW5rYd/view?usp=sharing

Assets in the project
Image
Image
Image
Image

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.32.0, on macOS 15.4.1 24E263 darwin-arm64, locale ru-RU) [653ms]
    • Flutter version 3.32.0 on channel stable at /Users/denis/Development/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision be698c48a6 (8 days ago), 2025-05-19 12:59:14 -0700
    • Engine revision 1881800949
    • Dart version 3.8.0
    • DevTools version 2.45.1

[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0) [3,1s]
    • Android SDK at /Users/denis/Library/Android/sdk
    • Platform android-35, build-tools 35.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.3+-79915917-b509.11)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 16.3) [2,8s]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16E140
    • CocoaPods version 1.16.2

[✓] Chrome - develop for the web [14ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2024.2) [13ms]
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.3+-79915917-b509.11)

[✓] VS Code (version 1.100.2) [11ms]
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.110.0

Metadata

Metadata

Assignees

Labels

a: imagesLoading, displaying, rendering imagesc: performanceRelates to speed or footprint issues (see "perf:" labels)f: scrollingViewports, list views, slivers, etc.found in release: 3.32Found to occur in 3.32found in release: 3.33Found to occur in 3.33frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-androidAndroid applications specificallyr: fixedIssue is closed as already fixed in a newer versionteam-androidOwned by Android platform team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions