flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
166.18k stars 27.45k forks source link

`FloatingActionButton` automatically gets positioned according to `MediaQuery.viewPadding` instead of using `MediaQuery.padding` like the `ListView` does #145680

Open jb3rndt opened 7 months ago

jb3rndt commented 7 months ago

Steps to reproduce

  1. Run flutter create --sample=material.NavigationBar.3 mysample
  2. Set extendBody: true on the main Scaffold
  3. Give a RootView's Scaffold a FAB (4. Replace the RootView's Scaffold Body with a ListView)

Expected results

I expect the FAB to not be covered by the NavBar, but to be placed above it. Sure, one can solve it with a padding but there already exists a mechanism that the main Scaffold calculates the Navbar size and adds that size to MediaQuery.padding so that the ListView in the body has enough scroll space for all items to be visible. I found out that the FAB uses MediaQuery.viewPadding instead, so copying the MediaQuery.padding (which contains the calculated navbar size) of the main Scaffold body to MediaQuery.viewPadding achieves the desired effect. So I'm wondering if that is even advisable and why the FAB doesnt use MediaQuery.padding like the ListView does.

Actual results

The FAB doesnt use MediaQuery.padding to prevent obscuration by the navbar.

Code sample

Code sample ```dart import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; /// Flutter code sample for [NavigationBar] with nested [Navigator] destinations. /// `flutter create --sample=material.NavigationBar.3 mysample` void main() { WidgetsFlutterBinding.ensureInitialized(); // make navigation bar transparent SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( systemNavigationBarColor: Colors.transparent, ), ); // make flutter draw behind navigation bar SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); runApp(const MaterialApp(home: Home())); } class Home extends StatefulWidget { const Home({super.key}); @override State createState() => _HomeState(); } class _HomeState extends State with TickerProviderStateMixin { static const List allDestinations = [ Destination(0, 'Teal', Icons.home, Colors.teal), Destination(1, 'Cyan', Icons.business, Colors.cyan), Destination(2, 'Orange', Icons.school, Colors.orange), Destination(3, 'Blue', Icons.flight, Colors.blue), ]; late final List> navigatorKeys; late final List destinationViews; int selectedIndex = 0; @override void initState() { super.initState(); navigatorKeys = List>.generate( allDestinations.length, (int index) => GlobalKey(), ).toList(); destinationViews = allDestinations.map( (Destination destination) { return DestinationView( destination: destination, navigatorKey: navigatorKeys[destination.index], ); }, ).toList(); } @override Widget build(BuildContext context) { return NavigatorPopHandler( onPop: () { final NavigatorState navigator = navigatorKeys[selectedIndex].currentState!; navigator.pop(); }, child: Scaffold( extendBody: true, body: Builder(builder: (bodyContext) { return MediaQuery( // This is important to make the nested FAB work. If this is removed, the FAB will be hidden behind the navigation bar. data: MediaQuery.of(bodyContext).copyWith(viewPadding: MediaQuery.of(bodyContext).padding), child: Stack( fit: StackFit.expand, children: allDestinations.map( (Destination destination) { final int index = destination.index; final Widget view = destinationViews[index]; if (index == selectedIndex) { return Offstage(offstage: false, child: view); } else { return Offstage(child: view); } }, ).toList(), ), ); }), bottomNavigationBar: NavigationBar( backgroundColor: Colors.white.withOpacity(0.7), selectedIndex: selectedIndex, onDestinationSelected: (int index) { setState(() { selectedIndex = index; }); }, destinations: allDestinations.map( (Destination destination) { return NavigationDestination( icon: Icon(destination.icon, color: destination.color), label: destination.title, ); }, ).toList(), ), ), ); } } class Destination { const Destination(this.index, this.title, this.icon, this.color); final int index; final String title; final IconData icon; final MaterialColor color; } class RootPage extends StatelessWidget { const RootPage({super.key, required this.destination}); final Destination destination; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('${destination.title} RootPage - /'), backgroundColor: destination.color, foregroundColor: Colors.white, ), backgroundColor: destination.color[50], floatingActionButton: destination.index == 0 ? FloatingActionButton(onPressed: () {}, child: const Icon(Icons.add)) : null, body: ListView.builder( itemCount: 15, // 0 to 14 should be visible itemBuilder: (context, index) => ListTile(title: Text(index.toString())), ), ); } } class DestinationView extends StatefulWidget { const DestinationView({ super.key, required this.destination, required this.navigatorKey, }); final Destination destination; final Key navigatorKey; @override State createState() => _DestinationViewState(); } class _DestinationViewState extends State { @override Widget build(BuildContext context) { return Navigator( key: widget.navigatorKey, onGenerateRoute: (RouteSettings settings) { return MaterialPageRoute( settings: settings, builder: (BuildContext context) { switch (settings.name) { case '/': return RootPage(destination: widget.destination); } assert(false); return const SizedBox(); }, ); }, ); } } ```

Screenshots or Video

Screenshots / Video demonstration This is the base case (without copying any MediaQuery.padding) -> The FAB is hidden behind the Navbar while the ListView automatically has enough padding so that the last item (14) is visible above the navbar. Here, `MediaQuery.padding` of the main Scaffold body is copied to `MediaQuery.viewPadding`, so the FAB is correctly placed automatically.

Logs

Logs ```console [Paste your logs here] ```

Flutter Doctor output

Doctor output ```console [✓] Flutter (Channel stable, 3.19.2, on Microsoft Windows [Version 10.0.19045.4170], locale de-DE) • Flutter version 3.19.2 on channel stable at C:\src\flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision 7482962148 (4 weeks ago), 2024-02-27 16:51:22 -0500 • Engine revision 04817c99c9 • Dart version 3.3.0 • DevTools version 2.31.1 [✓] Windows Version (Installed version of Windows is version 10 or higher) [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at C:\Users\janni\AppData\Local\Android\sdk • Platform android-34, build-tools 34.0.0 • Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java • Java version OpenJDK Runtime Environment (build 17.0.6+0-b2043.56-9586694) • All Android licenses accepted. [✗] 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 Windows apps ✗ Visual Studio not installed; this is necessary to develop Windows apps. Download at https://visualstudio.microsoft.com/downloads/. Please install the "Desktop development with C++" workload, including all of its default components [✓] Android Studio (version 2022.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 17.0.6+0-b2043.56-9586694) [✓] VS Code, 64-bit edition (version 1.87.2) • VS Code at C:\Program Files\Microsoft VS Code • Flutter extension version 3.84.0 [✓] Connected device (3 available) • sdk gphone64 x86 64 (mobile) • emulator-5554 • android-x64 • Android 14 (API 34) (emulator) • Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.19045.4170] • Edge (web) • edge • web-javascript • Microsoft Edge 122.0.2365.92 [✓] Network resources • All expected network resources are available. ! Doctor found issues in 2 categories. ```
danagbemava-nc commented 7 months ago

Reproducible using the code sample provided above.

flutter doctoror -v ``` [!] Flutter (Channel stable, 3.19.4, on macOS 14.3.1 23D60 darwin-arm64, locale en-GB) • Flutter version 3.19.4 on channel stable at /Users/nexus/dev/sdks/flutter ! Warning: `flutter` on your path resolves to /Users/nexus/dev/sdks/flutters/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/nexus/dev/sdks/flutter. Consider adding /Users/nexus/dev/sdks/flutter/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/nexus/dev/sdks/flutters/bin/dart, which is not inside your current Flutter SDK checkout at /Users/nexus/dev/sdks/flutter. Consider adding /Users/nexus/dev/sdks/flutter/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision 68bfaea224 (5 days ago), 2024-03-20 15:36:31 -0700 • Engine revision a5c24f538d • Dart version 3.3.2 • DevTools version 2.31.1 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at /Users/nexus/Library/Android/sdk • Platform android-34, build-tools 34.0.0 • Java binary at: /Users/nexus/Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 15.3) • Xcode at /Applications/Xcode-15.3.0.app/Contents/Developer • Build 15E204a • CocoaPods version 1.14.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2023.1) • Android Studio at /Users/nexus/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 17.0.7+0-17.0.7b1000.6-10550314) [✓] IntelliJ IDEA Ultimate Edition (version 2023.2.5) • IntelliJ at /Users/nexus/Applications/IntelliJ IDEA Ultimate.app • Flutter plugin version 77.2.2 • Dart plugin version 232.10286 [✓] VS Code (version 1.87.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.84.0 [✓] Connected device (3 available) • Dean’s iPad (mobile) • 00008103-000825C811E3401E • ios • iOS 17.4.1 21E236 • macOS (desktop) • macos • darwin-arm64 • macOS 14.3.1 23D60 darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 123.0.6312.58 [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. ``` ``` [✓] Flutter (Channel master, 3.21.0-14.0.pre.9, on macOS 14.3.1 23D60 darwin-arm64, locale en-GB) • Flutter version 3.21.0-14.0.pre.9 on channel master at /Users/nexus/dev/sdks/flutters • Upstream repository https://github.com/flutter/flutter.git • Framework revision 7f06c263a4 (9 hours ago), 2024-03-25 00:33:34 -0400 • Engine revision ae758d5463 • Dart version 3.4.0 (build 3.4.0-268.0.dev) • DevTools version 2.34.0-dev.12 [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at /Users/nexus/Library/Android/sdk • Platform android-34, build-tools 34.0.0 • Java binary at: /Users/nexus/Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 15.3) • Xcode at /Applications/Xcode-15.3.0.app/Contents/Developer • Build 15E204a • CocoaPods version 1.14.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2023.1) • Android Studio at /Users/nexus/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 17.0.7+0-17.0.7b1000.6-10550314) [✓] IntelliJ IDEA Ultimate Edition (version 2023.2.5) • IntelliJ at /Users/nexus/Applications/IntelliJ IDEA Ultimate.app • Flutter plugin version 77.2.2 • Dart plugin version 232.10286 [✓] VS Code (version 1.87.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.84.0 [✓] Connected device (4 available) • Dean’s iPad (mobile) • 00008103-000825C811E3401E • ios • iOS 17.4.1 21E236 • macOS (desktop) • macos • darwin-arm64 • macOS 14.3.1 23D60 darwin-arm64 • Mac Designed for iPad (desktop) • mac-designed-for-ipad • darwin • macOS 14.3.1 23D60 darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 123.0.6312.58 [✓] Network resources • All expected network resources are available. • No issues found! ```
HansMuller commented 7 months ago

Have you tried setting https://api.flutter.dev/flutter/material/FloatingActionButtonLocation-class.html?

jb3rndt commented 7 months ago

If I want the default FloatingActionButton (FloatingActionButtonLocation.endFloat) to not be covered by the navbar, I found no other possible FloatingActionButtonLocation (without using techniques discussed in the initial post). I compared how all the different FloatingActionButtonLocation behave when copying the Mediquery.padding as described vs not.

---------Default--------- Copying Mediquery.padding ---------Default--------- Copying Mediquery.padding
centerDocked centerDocked centerDocked centerDocked centerFloat centerFloat centerFloat centerFloat
centerTop centerTop centerTop centerTop endContained endContained endContained endContained
endDocked endDocked endDocked endDocked endFloat endFloat endFloat endFloat
endTop endTop endTop endTop miniCenterDocked miniCenterDocked miniCenterDocked miniCenterDocked
miniCenterFloat miniCenterFloat miniCenterFloat miniCenterFloat miniCenterTop miniCenterTop miniCenterTop miniCenterTop
miniEndDocked miniEndDocked miniEndDocked miniEndDocked miniEndFloat miniEndFloat miniEndFloat miniEndFloat
miniEndTop miniEndTop miniEndTop miniEndTop miniStartDocked miniStartDocked miniStartDocked miniStartDocked
miniStartFloat miniStartFloat miniStartFloat miniStartFloat miniStartTop miniStartTop miniStartTop miniStartTop
startDocked startDocked startDocked startDocked startFloat startFloat startFloat startFloat
startTop startTop startTop startTop
HansMuller commented 7 months ago

Sorry - because I'm probably missing something obvious, but: don't you want FloatingActionButtonLocation.endFloat?

jb3rndt commented 6 months ago

No problem, I'll try to rephrase the core of what this issue is about.

So this isnt about any FloatingActionButtonLocation in particular, but rather about offsets that are specified by MediaQuery. The ListView (by default) internally adds a bottom padding equal to MediaQuery.of(context).padding.bottom to prevent its list children to be concealed by some system elements or a navigation bar. I would expect the FloatingActionButton to do the same because in the case of a nested scaffold (like in https://api.flutter.dev/flutter/material/NavigationBar-class.html#material.NavigationBar.3) a navigation bar would cover a FloatingActionButton that only exists in one of the tab's scaffold (if extendBody is true on the main scaffold, to be exact). Instead, the FloatingActionButton uses the MediaQuery.viewPadding, which is confusing / requires workarounds. I'm wondering why that is the case.

I'm unsure if that helps