Small details that build taste in Flutter.

curated by Kamran BekirovKamran Bekirov

Add scrollbar to scrollable widgets

Flutter automatically adds a scrollbar to most vertically scrollable widgets on desktop platforms (including web when opened via a desktop browser).

On mobile, it's your responsibility, and you need to be careful: if you add one manually, it clashes with the automatic desktop scrollbar on the same page, and both stop working right.

There are two approaches to avoid that:

Manual: wrap scrollable widgets with MagicalScrollbar. Best when you only need scrollbars in a few places.

Copy this widget:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
  
class MagicalScrollbar extends StatelessWidget {
  final Widget child;
  final ScrollController? controller;
  
  const MagicalScrollbar({
    super.key,
    required this.child,
    this.controller,
  });
  
  @override
  Widget build(BuildContext context) {
    final bool isMobile = [TargetPlatform.android, TargetPlatform.iOS]
        .contains(defaultTargetPlatform);
  
    if (isMobile) {
      return Scrollbar(
        controller: controller,
        child: child,
      );
    } else {
      return child;
    }
  }
}

Then use it:

If your scrollable has its own controller, just pass it too:

MagicalScrollbar(
    controller: controller,
    child: ListView.builder(
        controller: controller,
        ...
    ),
),

Automatic: enable it everywhere at once by overriding MaterialApp.scrollBehavior. Best when you want scrollbars app-wide.

Copy this custom scroll behavior:

class AlwaysScrollbarBehavior extends MaterialScrollBehavior {
  const AlwaysScrollbarBehavior();
  
  @override
  Widget buildScrollbar(context, child, details) {
    return Scrollbar(
      controller: details.controller,
      child: child,
    );
  }
}

Then set it:

MaterialApp(
    ...
    scrollBehavior: const AlwaysScrollbarBehavior(),
    ...
);

Some scrollable widgets like single-line EditableText, PageView, ListWheelScrollView, NestedScrollView, and DropdownButton stay scrollbar-free even with an app-wide override, because each overrides the inherited behavior locally to drop the scrollbar, since one would misrepresent how you actually interact with them.

Kamran Bekirov
Kamran Bekirov

Want this level of care in your Flutter apps?

Work With Me