Skip to content

Micro-stutters when scrolling on Android after updating to Flutter 3.3 #110885

Description

@ercantomac

Details

Scrolling in my app got worse after updating to version 3.3. I see micro-stutters while scrolling in my app on Android that weren't there before (in version 3.0.5).

WhatsApp.Video.2022-09-02.at.23.44.26_Trim2.mp4

I also recorded a timeline trace.

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations(<DeviceOrientation>[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
  SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
  SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light.copyWith(systemNavigationBarColor: const Color(0x00000000)));
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static NetworkController networkController = NetworkController();
  static Map<String, List<Comment>> visitedPosts = <String, List<Comment>>{};

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.cyan,
        pageTransitionsTheme: const PageTransitionsTheme(builders: <TargetPlatform, PageTransitionsBuilder>{
          TargetPlatform.android: CupertinoPageTransitionsBuilder(),
          TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
        }),
        useMaterial3: true,
        cupertinoOverrideTheme: CupertinoThemeData(
          primaryColor: Theme.of(context).colorScheme.primary,
          brightness: Brightness.light,
          textTheme: const CupertinoTextThemeData(),
        ),
      ),
      darkTheme: ThemeData(
        primarySwatch: Colors.cyan,
        brightness: Brightness.dark,
        pageTransitionsTheme: const PageTransitionsTheme(builders: <TargetPlatform, PageTransitionsBuilder>{
          TargetPlatform.android: CupertinoPageTransitionsBuilder(),
          TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
        }),
        useMaterial3: true,
        cupertinoOverrideTheme: CupertinoThemeData(
          primaryColor: Theme.of(context).colorScheme.primary,
          brightness: Brightness.dark,
          textTheme: const CupertinoTextThemeData(),
        ),
      ),
      scrollBehavior: ScrollBehaviorModified(),
      home: const MyHomePage(),
    );
  }
}

class ScrollBehaviorModified extends CupertinoScrollBehavior {
  @override
  ScrollPhysics getScrollPhysics(BuildContext context) {
    return CustomScrollPhysics();
  }

  @override
  Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) {
    return StretchingOverscrollIndicator(
      axisDirection: details.direction,
      child: child,
    );
  }
}

class CustomScrollPhysics extends ClampingScrollPhysics {
  @override
  Simulation? createBallisticSimulation(ScrollMetrics position, double velocity) {
    final Tolerance tolerance = this.tolerance;
    if (velocity.abs() >= tolerance.velocity || position.outOfRange) {
      return BouncingScrollSimulation(
        spring: spring,
        position: position.pixels,
        velocity: velocity,
        leadingExtent: position.minScrollExtent,
        trailingExtent: position.maxScrollExtent,
        tolerance: tolerance,
      );
    }
    return null;
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  final int _pageSize = 25;
  late String _currentListing = 'Hot';
  late String? _after = '';
  final List<Post> _posts = <Post>[];
  final ScrollController _scrollController = ScrollController();
  final PagingController _pagingController = PagingController(firstPageKey: 0);
  final ValueNotifier<bool> _blur = ValueNotifier<bool>(false);

  @override
  void initState() {
    super.initState();
    initialize();
  }

  void initialize() async {
    await MyApp.networkController.getToken();
    _pagingController.addPageRequestListener((pageKey) {
      _fetchPage(pageKey);
    });
    _fetchPage(0);
  }

  Future<void> _fetchPage(int pageKey) async {
    try {
      final newItems = await MyApp.networkController.getListing('/${_currentListing.toLowerCase()}', after: _after!);
      final bool isLastPage = newItems.length < _pageSize;
      _after = '?after=${newItems.removeAt(0)}';
      for (int i = 0; i < _pageSize; i++) {
        _posts.add(newItems[i]);
      }
      if (isLastPage) {
        _pagingController.appendLastPage(newItems);
      } else {
        final num nextPageKey = pageKey + newItems.length;
        _pagingController.appendPage(newItems, nextPageKey);
      }
      setState(() {});
    } catch (error) {
      _pagingController.error = error;
    }
  }

  late String _listingHeader = 'Hot';

  void changeListing(String listing) async {
    if (listing.startsWith('top')) {
      _listingHeader = 'Top • ${listing.substring(7)}';
    } else if (listing.startsWith('controversial')) {
      _listingHeader = 'Controversial • ${listing.substring(17)}';
    } else {
      _listingHeader = listing;
    }
    setState(() {
      _posts.clear();
      _after = '';
      _currentListing = listing;
    });
    _pagingController.refresh();
  }

  @override
  void dispose() {
    _scrollController.dispose();
    _pagingController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: RawScrollbar(
        interactive: true,
        shape: const StadiumBorder(),
        controller: _scrollController,
        child: CustomScrollView(
          controller: _scrollController,
          slivers: <Widget>[
            CupertinoSliverNavigationBar(
              largeTitle: Text(_listingHeader),
              middle: GestureDetector(
                  onTap: () {
                    _blur.value = true;
                    _scrollController
                        .animateTo(0.0,
                            duration: Duration(milliseconds: 400 + (_scrollController.offset ~/ 75)),
                            curve: Curves.easeOutCubic)
                        .then((value) {
                      _blur.value = false;
                    });
                  },
                  child: const Text('Homepage')),
            ),
            if (_posts.isEmpty) const SliverFillRemaining(child: Center(child: CircularProgressIndicator.adaptive())),
            if (_posts.isNotEmpty)
              CupertinoSliverRefreshControl(
                onRefresh: () {
                  return Future<void>(() => _pagingController.refresh());
                },
              ),
            if (_posts.isNotEmpty)
              SliverStack(
                children: <Widget>[
                  PagedSliverList(
                    pagingController: _pagingController,
                    builderDelegate: PagedChildBuilderDelegate(
                      itemBuilder: (BuildContext context, Object? item, int index) {
                        return PostBuilder(
                          post: _posts[index],
                          pageTitle: 'Homepage',
                        );
                      },
                    ),
                  ),
                  SliverPositioned.fill(
                    child: ValueListenableBuilder<bool>(
                      valueListenable: _blur,
                      builder: (BuildContext context, bool value, Widget? child) {
                        return value
                            ? AnimatedBuilder(
                                animation: _scrollController,
                                builder: (BuildContext context, Widget? child) {
                                  return BackdropFilter(
                                      filter: ImageFilter.blur(
                                          sigmaX: 0.001, sigmaY: 8.0 - (8.0 / max(1.0, _scrollController.offset / 50.0))),
                                      child: child);
                                },
                                child: Container(),
                              )
                            : Container();
                      },
                    ),
                  ),
                ],
              ),
          ],
        ),
      ),
    );
  }
}

Target Platform: Android
Target OS version/browser: Android 12
Devices: Motorola Edge 30

Logs

Logs

info - Specify type annotations - lib\Builder\post_builder.dart:67:42 - always_specify_types
info - Specify type annotations - lib\Builder\post_builder.dart:88:56 - always_specify_types
info - Specify type annotations - lib\Builder\post_builder.dart:105:25 - always_specify_types
info - Specify type annotations - lib\Builder\post_builder.dart:109:36 - always_specify_types
info - Specify type annotations - lib\Builder\post_builder.dart:109:37 - always_specify_types
info - Do not use BuildContexts across async gaps - lib\Builder\post_builder.dart:245:33 - use_build_context_synchronously
info - Do not use BuildContexts across async gaps - lib\Builder\post_builder.dart:247:33 - use_build_context_synchronously
info - Do not use BuildContexts across async gaps - lib\Builder\post_builder.dart:282:33 - use_build_context_synchronously
info - Do not use BuildContexts across async gaps - lib\Builder\post_builder.dart:284:33 - use_build_context_synchronously
info - Do not use BuildContexts across async gaps - lib\Builder\post_builder.dart:321:33 - use_build_context_synchronously
info - Do not use BuildContexts across async gaps - lib\Builder\post_builder.dart:324:33 - use_build_context_synchronously
info - Avoid using null in if null operators - lib\Model\comment.dart:152:22 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:154:26 - unnecessary_null_in_if_null_operators
info - Specify type annotations - lib\Model\comment.dart:156:94 - always_specify_types
info - Avoid using null in if null operators - lib\Model\comment.dart:159:26 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:160:30 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:161:20 - unnecessary_null_in_if_null_operators
info - Specify type annotations - lib\Model\comment.dart:164:105 - always_specify_types
info - Avoid using null in if null operators - lib\Model\comment.dart:165:16 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:166:13 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:169:17 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:170:19 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:172:19 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:173:17 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:174:21 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:175:21 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:176:22 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:177:19 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:178:16 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:179:25 - unnecessary_null_in_if_null_operators
info - Specify type annotations - lib\Model\comment.dart:182:108 - always_specify_types
info - Avoid using null in if null operators - lib\Model\comment.dart:183:20 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:184:15 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:185:17 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:188:15 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:189:22 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:190:16 - unnecessary_null_in_if_null_operators
info - Specify type annotations - lib\Model\comment.dart:192:114 - always_specify_types
info - Avoid using null in if null operators - lib\Model\comment.dart:193:29 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:194:19 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:199:19 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:200:24 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:201:18 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:205:22 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:206:20 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:207:24 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:208:17 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:210:18 - unnecessary_null_in_if_null_operators
info - Specify type annotations - lib\Model\comment.dart:212:111 - always_specify_types
info - Avoid using null in if null operators - lib\Model\comment.dart:213:17 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:214:32 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:215:27 - unnecessary_null_in_if_null_operators
info - Avoid using null in if null operators - lib\Model\comment.dart:216:16 - unnecessary_null_in_if_null_operators
info - Specify type annotations - lib\Model\comment.dart:219:102 - always_specify_types
info - Avoid using null in if null operators - lib\Model\comment.dart:221:14 - unnecessary_null_in_if_null_operators
info - Avoid defining unused parameters in constructors - lib\Model\comment.dart:228:29 - avoid_unused_constructor_parameters
info - Specify type annotations - lib\Model\post.dart:243:108 - always_specify_types
info - Prefer using null aware operators - lib\Model\post.dart:255:20 - prefer_null_aware_operators
info - Specify type annotations - lib\Model\post.dart:264:103 - always_specify_types
info - Specify type annotations - lib\Model\post.dart:280:112 - always_specify_types
info - Specify type annotations - lib\Model\post.dart:308:63 - always_specify_types
info - Specify type annotations - lib\Model\post.dart:309:92 - always_specify_types
info - Specify type annotations - lib\Model\post.dart:315:109 - always_specify_types
info - Specify type annotations - lib\Model\post.dart:334:100 - always_specify_types
info - Specify type annotations - lib\Model\post.dart:431:65 - always_specify_types
info - Specify type annotations - lib\Model\post.dart:446:72 - always_specify_types
info - Avoid defining unused parameters in constructors - lib\Model\post.dart:539:31 - avoid_unused_constructor_parameters
info - Specify type annotations - lib\Model\post.dart:552:90 - always_specify_types
info - Specify type annotations - lib\Model\post.dart:574:63 - always_specify_types
info - Specify type annotations - lib\Screen\post_detail_screen.dart:40:12 - always_specify_types
info - Specify type annotations - lib\Screen\post_detail_screen.dart:63:26 - always_specify_types
info - Specify type annotations - lib\Screen\post_detail_screen.dart:117:60 - always_specify_types
info - Specify type annotations - lib\Screen\post_detail_screen.dart:134:29 - always_specify_types
info - Specify type annotations - lib\Screen\post_detail_screen.dart:138:40 - always_specify_types
info - Specify type annotations - lib\Screen\post_detail_screen.dart:138:41 - always_specify_types
info - Do not use BuildContexts across async gaps - lib\Screen\post_detail_screen.dart:223:37 - use_build_context_synchronously
info - Do not use BuildContexts across async gaps - lib\Screen\post_detail_screen.dart:225:37 - use_build_context_synchronously
info - Do not use BuildContexts across async gaps - lib\Screen\post_detail_screen.dart:259:37 - use_build_context_synchronously
info - Do not use BuildContexts across async gaps - lib\Screen\post_detail_screen.dart:261:37 - use_build_context_synchronously
info - Do not use BuildContexts across async gaps - lib\Screen\post_detail_screen.dart:297:37 - use_build_context_synchronously
info - Do not use BuildContexts across async gaps - lib\Screen\post_detail_screen.dart:300:37 - use_build_context_synchronously
info - The value of the field '_before' isn't used - lib\Screen\subreddit_screen.dart:24:29 - unused_field
info - Specify type annotations - lib\Screen\subreddit_screen.dart:27:9 - always_specify_types
info - Specify type annotations - lib\Screen\subreddit_screen.dart:27:46 - always_specify_types
info - Specify type annotations - lib\Screen\subreddit_screen.dart:33:47 - always_specify_types
info - Specify type annotations - lib\Screen\subreddit_screen.dart:44:7 - always_specify_types
info - Specify type annotations - lib\Screen\subreddit_screen.dart:120:32 - always_specify_types
info - Specify type annotations - lib\Screen\subreddit_screen.dart:130:23 - always_specify_types
info - Specify type annotations - lib\Screen\subreddit_screen.dart:132:42 - always_specify_types
info - The value of the field '_before' isn't used - lib\main.dart:126:29 - unused_field
info - Specify type annotations - lib\main.dart:129:9 - always_specify_types
info - Specify type annotations - lib\main.dart:129:68 - always_specify_types
info - Specify type annotations - lib\main.dart:167:47 - always_specify_types
info - Specify type annotations - lib\main.dart:175:7 - always_specify_types
info - Specify type annotations - lib\main.dart:253:32 - always_specify_types
info - Specify type annotations - lib\main.dart:311:19 - always_specify_types
info - Specify type annotations - lib\main.dart:314:38 - always_specify_types
info - Specify type annotations - lib\network_controller.dart:59:7 - always_specify_types
info - Specify type annotations - lib\network_controller.dart:92:7 - always_specify_types

112 issues found. (ran in 9.6s)

[√] Flutter (Channel stable, 3.3.0, on Microsoft Windows [Version 10.0.22000.856], locale en-US)
• Flutter version 3.3.0 on channel stable at C:\src\flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision ffccd96 (4 days ago), 2022-08-29 17:28:57 -0700
• Engine revision 5e9e0e0aa8
• Dart version 2.18.0
• DevTools version 2.15.0

[√] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
• Android SDK at ***
• Platform android-33, build-tools 33.0.0
• Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
• Java version OpenJDK Runtime Environment (build 11.0.12+7-b1504.28-7817840)
• All Android licenses accepted.

[X] Chrome - develop for the web (Cannot find Chrome executable at .\Google\Chrome\Application\chrome.exe)
! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.

[√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.3.1)
• Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
• Visual Studio Community 2022 version 17.3.32811.315
• Windows 10 SDK version 10.0.19041.0

[√] Android Studio (version 2021.2)
• Android Studio at C:\Program Files\Android\Android Studio
• 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 11.0.12+7-b1504.28-7817840)

[√] VS Code (version 1.70.2)
• VS Code at ***
• Flutter extension version 3.48.0

[√] Connected device (3 available)
• motorola edge 30 (mobile) • *** • android-arm64 • Android 12 (API 31)
• Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.22000.856]
• Edge (web) • edge • web-javascript • Microsoft Edge 104.0.1293.63

[√] HTTP Host Availability
• All required HTTP hosts are available

! Doctor found issues in 1 category.

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work listc: performanceRelates to speed or footprint issues (see "perf:" labels)engineflutter/engine related. See also e: labels.f: scrollingViewports, list views, slivers, etc.found in release: 3.3Found to occur in 3.3found in release: 3.4Found to occur in 3.4has reproducible stepsThe issue has been confirmed reproducible and is ready to work onr: fixedIssue is closed as already fixed in a newer version

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions