proper way to get widget height in SafeArea
Asked Answered
P

7

69

I'm trying to get the height of widget but it prints the same values

I/flutter (19253): full height: 976.0
I/flutter (19253): safe height: 976.0

I guessed the second value should be smaller because the Container placed below the status bar. What I'm doing wrong? I need height because in this container will be Wrap widget (actually ReorderableWrap https://pub.dartlang.org/packages/reorderables#-readme-tab-) with 36 cards, 3 rows by 12 cards and cards height must be 1/3 of its container.

I didn't find good Reorderable Grid. But anyway my question why a Container in a safe area has the same height as Container that fills the entire screen?

import 'package:flutter/material.dart';

void main() {
  runApp(_MyApp());
}

class _MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(body: _Body()),
    );
  }
}

class _Body extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('full height: ${MediaQuery.of(context).size.height}');
    return Container(
      constraints: BoxConstraints.expand(),
      decoration: BoxDecoration(color: Colors.red),
      child: SafeArea(
        child: _SafeHeightWidget(),
      ),
    );
  }
}

class _SafeHeightWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('safe height: ${MediaQuery.of(context).size.height}');
    return Container(
      color: Colors.lightBlue,
    );
  }
}
Persuader answered 10/4, 2019 at 10:50 Comment(0)
N
56

You can always use LayoutBuilder for such cases.

child: SafeArea(
        child: new LayoutBuilder(
            builder: (BuildContext context, BoxConstraints constraints) {
              // constraints variable has the size info
              return Container();
            }
        ),
      ),

for more info: https://www.youtube.com/watch?v=IYDVcriKjsw

Ness answered 10/4, 2019 at 11:40 Comment(1)
Only problem of this is that when you show the keyboard the LayoutBuilder re renders and you loose the actual height of your screen.Quacksalver
B
135

If you only need the safe area top padding and not the height of the safe area widget child:

final safePadding = MediaQuery.of(context).padding.top;

or (without context object)

final safePadding = WidgetsBinding.instance.window.padding.top;

If you need AppBar heigh:

final appBarHeight = AppBar().preferredSize.height;
/// or kBottomNavigationBarHeight
Bagging answered 21/10, 2019 at 8:18 Comment(4)
If you also need AppBar height AppBar().preferredSize.heightLory
If you don't have a context you can use WidgetsBinding.instance.window.padding.top.Glossectomy
@OvidiuUşvat this is very valuable to know how to access size outside contextNeal
The proper way to obtain appbar height is import import 'package:flutter/material.dart';. Then call constant variables like kToolbarHeight, kBottomNavigationBarHeight, etc.Bromley
N
56

You can always use LayoutBuilder for such cases.

child: SafeArea(
        child: new LayoutBuilder(
            builder: (BuildContext context, BoxConstraints constraints) {
              // constraints variable has the size info
              return Container();
            }
        ),
      ),

for more info: https://www.youtube.com/watch?v=IYDVcriKjsw

Ness answered 10/4, 2019 at 11:40 Comment(1)
Only problem of this is that when you show the keyboard the LayoutBuilder re renders and you loose the actual height of your screen.Quacksalver
C
32

Flutter 2.5

Add this at top level of Widget...and you can get exact SafeArea height.

final availableHeight = MediaQuery.of(context).size.height -
    AppBar().preferredSize.height -
    MediaQuery.of(context).padding.top -
    MediaQuery.of(context).padding.bottom;
Commission answered 9/7, 2020 at 22:47 Comment(1)
And if you have a bottom navigation bar you should also remove the size of it like this: final availableHeight = MediaQuery.of(context).size.height - AppBar().preferredSize.height - MediaQuery.of(context).padding.top - MediaQuery.of(context).padding.bottom - kBottomNavigationBarHeight; Resurrectionism
D
14

Flutter 2.5

Had a similar challenge.

Learned the hard way:

Do not wrap SafeArea inside main.dart application, instead wrap your Scaffold with SafeArea Widget (even better create a ReusableScaffold with wrapped SafeArea) and then you can extract the MediaQuery height with SafeArea below code:

var availableHeight = MediaQuery.of(context).size.height -
    AppBar().preferredSize.height -
    MediaQuery.of(context).padding.top -
    MediaQuery.of(context).padding.bottom;

The trick is if your whole main.app child is wrapped with SafeArea the padding.top and padding.bottom will always remain 0 because that's how SafeArea works - it makes padding EdgeInsets.zero - you can find this information inside SafeArea documentation.

But if you calculate the height without wrapping safeArea you will get the padding of top & bottom which allows to get the remaining height without SafeArea

example:

I/flutter (12472): full height 712.0 -> full height
I/flutter (12472): available height 618.5 -> remaining height minus safeArea & appBar
I/flutter (12472): MediaQuery.of(context).padding.top 37.5 -> safeArea height
I/flutter (12472): MediaQuery.of(context).padding.bottom 0.0 -> Im using android with 720 height, on iPhone X it would not zero
I/flutter (12472): viewPadding EdgeInsets(0.0, 37.5, 0.0, 0.0) -> shows all padding insets
Deputation answered 20/9, 2021 at 13:39 Comment(0)
S
8

to get the size of the SafeArea, you need to encapsulate inside a LayoutBuilder , and use constraints.maxHeight

look at your own example modified:

I/flutter (20405): full Screen height: 683.4285714285714
I/flutter (20405): Screen height: 683.4285714285714
I/flutter (20405): Real safe height: 659.4285714285714

class _MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(body: _Body()),
    );
  }
}

class _Body extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('full Screen height: ${MediaQuery.of(context).size.height}');
    return Container(
      constraints: BoxConstraints.expand(),
      decoration: BoxDecoration(color: Colors.red),
      child: SafeArea(
        child: _SafeHeightWidget(),
      ),
    );
  }
}

class _SafeHeightWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        print('Screen height: ${MediaQuery.of(context).size.height}');
        print('Real safe height: ${constraints.maxHeight}');
        return Container(
          color: Colors.lightBlue,
        );
      },
    );
  }
}
Sulphuryl answered 29/2, 2020 at 23:40 Comment(0)
C
2

As per flutter, 'window' is deprecated and shouldn't be used. Look up the current FlutterView from the context via View.of(context) or consult the PlatformDispatcher directly instead. Deprecated to prepare for the upcoming multi-window support. This feature was deprecated after v3.7.0-32.0.pre.

  final safepadding = WidgetsBinding.instance.window.padding.top ;

to

  final safepadding = WidgetsBinding.instance.platformDispatcher.implicitView!.padding.top;
Constantan answered 30/7, 2023 at 8:20 Comment(0)
B
0

Here is how to get the safe area size in a neat way:

Size safeSize = MediaQuery.of(context).padding.deflateSize(MediaQuery.of(context).size);

deflateSize will take its argument and subtract itself from it. The result is returned.

Boyett answered 3/6 at 5:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.