Xuan
Xuan

Reputation: 746

How to constraints width and height of `Scaffold` (useful for Web and Desktop)

The screenshot below is a Scaffold on Desktop (should be similar on browser):

enter image description here

Is it possible to constraints Scaffold to a min size (width and height)?

If screen width is smaller than the min width, a horizontal scrollbar appears to move left and right.

If screen height is smaller than the min height, a vertical scrollbar appears to move up and down.

Ex. designers in our project want a min width of 960px on browser.

Upvotes: 2

Views: 3740

Answers (1)

Benjamin Lee
Benjamin Lee

Reputation: 1224

Here's an example main.dart that uses LayoutBuilder to constrain the scaffold (both the body and the appBar) to a min of 480dp, and if the width constraints are less than that, wraps the scaffold inside a horizontal ScrollView with a ScrollBar. And if the constraint height is less than 480dp, it wraps scaffold (which may already be wrapped or not) in a vertical scroll.

If both width and height are less than 480dp, 2 scrollbars are visible. In this case, the widget tree must be ScrollBar > ScrollBar > ScrollView > ScrollView. If widget tree is ScrollBar > ScrollView > ScrollBar > ScrollView, the nested Scrollbar is only visible when the parent ScrollBar is scrolled to the edge side.

import 'package:flutter/material.dart';

main() {
  runApp(MaterialApp(
    // set default isAlwaysShown, so don't need to set for individual Scrollbar.
    theme: ThemeData(scrollbarTheme: ScrollbarThemeData(isAlwaysShown: true)),
    home: App(),
  ));
}

class App extends StatefulWidget {
  @override
  AppState createState() => AppState();
}

class AppState extends State<App> {
  final minWidth = 480.0;
  final minHeight = 480.0;

  ScrollController _horizontalController = ScrollController();
  ScrollController _verticalController = ScrollController();

  @override
  void dispose() {
    _horizontalController.dispose();
    _verticalController.dispose();
    super.dispose();
  }

  Widget _buildScaffold() {
    return Scaffold(
      appBar: AppBar(title: Text('2D Scrollbars')),
      body: Container(color: Colors.amber),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(Icons.school), label: 'School'),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final scaffold = _buildScaffold();
    return LayoutBuilder(
      builder: (context, constraints) {
        final horizontalScrollbarEnabled = constraints.minWidth < minWidth;
        final verticalScrollbarEnabled = constraints.minHeight < minHeight;

        if (horizontalScrollbarEnabled && verticalScrollbarEnabled) {
          return Scrollbar(
            controller: _horizontalController,
            child: Scrollbar(
              // IMPORTANT: this scrollbar only handle notification of the vertical ScrollView.
              // The first ScrollView (depth = 0), is the horizontal one.
              // The second ScrollView (depth = 1), is the vertical one.
              // If notification.depth != 1, the notification is bubble up to horizontal Scrollbar.
              notificationPredicate: (notification) => notification.depth == 1,
              controller: _verticalController,
              child: SingleChildScrollView(
                controller: _horizontalController,
                scrollDirection: Axis.horizontal,
                child: SingleChildScrollView(
                  controller: _verticalController,
                  child: Container(
                    width: minWidth,
                    height: minHeight,
                    child: scaffold,
                  ),
                ),
              ),
            ),
          );
        } else if (horizontalScrollbarEnabled) {
          return Scrollbar(
            controller: _horizontalController,
            child: SingleChildScrollView(
              controller: _horizontalController,
              scrollDirection: Axis.horizontal,
              child: Container(
                width: minWidth,
                child: scaffold,
              ),
            ),
          );
        } else if (verticalScrollbarEnabled) {
          return Scrollbar(
            controller: _verticalController,
            child: SingleChildScrollView(
              controller: _verticalController,
              child: Container(
                height: minHeight,
                child: scaffold,
              ),
            ),
          );
        }

        return scaffold;
      },
    );
  }
}

Upvotes: 3

Related Questions