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
163.55k stars 26.9k forks source link

`Incorrect use of ParentDataWidget` exception may not appear visually in debug mode. #108186

Open bernaferrari opened 1 year ago

bernaferrari commented 1 year ago

I think Column(Padding(Expanded)) should fail in debug mode, because it is ultra hard to debug it is wrong in release mode in a complex layout:

image
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(body: Demo()),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Column(
      children: const [
        Padding(
          padding: EdgeInsets.all(16),
          child: Expanded(
            child: Text("This is only shown in debug mode!"),
          ),
        ),
      ],
    );
  }
}
➜ flutter doctor -v
[✓] Flutter (Channel stable, 3.0.5, on macOS 11.4 20F71 darwin-x64, locale
    en-BR)
    • Flutter version 3.0.5 at /usr/local/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision f1875d570e (9 days ago), 2022-07-13 11:24:16 -0700
    • Engine revision e85ea0e79c
    • Dart version 2.17.6
    • DevTools version 2.12.2
danagbemava-nc commented 1 year ago

Hi @bernaferrari, when I run your code sample, I got the error below in my console. I believe you should get the error below no matter how complex the UI is.

Is your request for the error screen to be shown in this case in debug mode?

logs ``` Launching lib/main.dart on iPhone 13 Pro Max in debug mode... Xcode build done. 33.5s Connecting to VM Service at ws://127.0.0.1:49657/EQJyWm2ye1g=/ws ════════ Exception caught by widgets library ═══════════════════════════════════ The following assertion was thrown while applying parent data.: Incorrect use of ParentDataWidget. The ParentDataWidget Expanded(flex: 1) wants to apply ParentData of type FlexParentData to a RenderObject, which has been set up to accept ParentData of incompatible type BoxParentData. Usually, this means that the Expanded widget has the wrong ancestor RenderObjectWidget. Typically, Expanded widgets are placed directly inside Flex widgets. The offending Expanded is currently placed inside a Padding widget. The ownership chain for the RenderObject that received the incompatible parent data was: RichText ← Text ← Expanded ← Padding ← Column ← Demo ← _BodyBuilder ← MediaQuery ← LayoutId-[<_ScaffoldSlot.body>] ← CustomMultiChildLayout ← ⋯ When the exception was thrown, this was the stack #0 RenderObjectElement._updateParentData. #1 RenderObjectElement._updateParentData #2 RenderObjectElement.attachRenderObject #3 RenderObjectElement.mount #4 MultiChildRenderObjectElement.mount ... Normal element mounting (16 frames) #20 Element.inflateWidget #21 MultiChildRenderObjectElement.inflateWidget #22 MultiChildRenderObjectElement.mount ... Normal element mounting (25 frames) #47 Element.inflateWidget #48 MultiChildRenderObjectElement.inflateWidget #49 MultiChildRenderObjectElement.mount ... Normal element mounting (108 frames) #157 Element.inflateWidget #158 MultiChildRenderObjectElement.inflateWidget #159 MultiChildRenderObjectElement.mount ... Normal element mounting (175 frames) #334 Element.inflateWidget #335 MultiChildRenderObjectElement.inflateWidget #336 MultiChildRenderObjectElement.mount ... Normal element mounting (377 frames) #713 Element.inflateWidget #714 Element.updateChild #715 RenderObjectToWidgetElement._rebuild #716 RenderObjectToWidgetElement.mount #717 RenderObjectToWidgetAdapter.attachToRenderTree. #718 BuildOwner.buildScope #719 RenderObjectToWidgetAdapter.attachToRenderTree #720 WidgetsBinding.attachRootWidget #721 WidgetsBinding.scheduleAttachRootWidget. (elided 11 frames from class _RawReceivePortImpl, class _Timer, dart:async, and dart:async-patch) ════════════════════════════════════════════════════════════════════════════════ ```
bernaferrari commented 1 year ago

I get the error, but visually it works and only breaks in release, which is tricky.

danagbemava-nc commented 1 year ago

I agree that it's tricky considering this exception is also not caught by the debugger.

I believe there are some instances where using the incorrect ParentDataWidget may show an exception on the screen, but this seems like one of the corner cases that are not captured.

I'll therefore be treating this as a bug and labeling for further investigation.

Reproducible on the latest stable and master.

Code sample can be found in https://github.com/flutter/flutter/issues/108186#issue-1315318039

flutter doctor -v ``` [✓] Flutter (Channel stable, 3.0.5, on macOS 12.4 21F79 darwin-arm, locale en-GB) • Flutter version 3.0.5 at /Users/nexus/dev/sdks/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision f1875d570e (13 days ago), 2022-07-13 11:24:16 -0700 • Engine revision e85ea0e79c • Dart version 2.17.6 • DevTools version 2.12.2 [✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0) • Android SDK at /Users/nexus/Library/Android/sdk • Platform android-33, build-tools 31.0.0 • Java binary at: /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8609683/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 13.4.1) • Xcode at /Applications/Xcode.app/Contents/Developer • CocoaPods version 1.11.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2021.2) • Android Studio at /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 11.0.12+0-b1504.28-7817840) [✓] Android Studio (version 2021.2) • Android Studio at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8609683/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 11.0.12+0-b1504.28-7817840) [✓] VS Code (version 1.69.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.44.0 [✓] Connected device (2 available) • macOS (desktop) • macos • darwin-arm64 • macOS 12.4 21F79 darwin-arm • Chrome (web) • chrome • web-javascript • Google Chrome 103.0.5060.134 [✓] HTTP Host Availability • All required HTTP hosts are available • No issues found! ``` ``` [✓] Flutter (Channel master, 3.1.0-0.0.pre.1865, on macOS 12.4 21F79 darwin-arm, locale en-GB) • Flutter version 3.1.0-0.0.pre.1865 on channel master at /Users/nexus/dev/sdks/flutters • Upstream repository https://github.com/flutter/flutter.git • Framework revision be14858464 (73 minutes ago), 2022-07-26 01:44:06 -0400 • Engine revision 8eca26d130 • Dart version 2.19.0 (build 2.19.0-33.0.dev) • DevTools version 2.15.0 [✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0) • Android SDK at /Users/nexus/Library/Android/sdk • Platform android-33, build-tools 31.0.0 • Java binary at: /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8609683/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 13.4.1) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 13F100 • CocoaPods version 1.11.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2021.2) • Android Studio at /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 11.0.12+0-b1504.28-7817840) [✓] Android Studio (version 2021.2) • Android Studio at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/212.5712.43.2112.8609683/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 11.0.12+0-b1504.28-7817840) [✓] VS Code (version 1.69.2) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.44.0 [✓] Connected device (2 available) • macOS (desktop) • macos • darwin-arm64 • macOS 12.4 21F79 darwin-arm • Chrome (web) • chrome • web-javascript • Google Chrome 103.0.5060.134 [✓] HTTP Host Availability • All required HTTP hosts are available • No issues found! ```
iontaroberto commented 1 year ago

To check before releasing on the store, you can run a test in release mode. If you do not have a production keystore (external customer) you can proceed with the creation of a new release keystore and modify the app/build.gradle. This code (see "preprod") worked for me (Flutter 3.0.4): command line: flutter run --flavor preprod --release

signingConfigs {
    debug {
        storeFile file('../keystores/debug.keystore')
        storePassword 'android'
        keyAlias 'androiddebugkey'
        keyPassword 'android'
    }
    releasetest {
        storeFile file('../keystores/releaseTest-keystore.jks')
        storePassword 'releaseTest'
        keyAlias 'releaseTest'
        keyPassword 'releaseTest'
    }
    release {
        keyAlias keystoreProperties['keyAlias']
        keyPassword keystoreProperties['keyPassword']
        storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
        storePassword keystoreProperties['storePassword']
    }
}

buildTypes {
    debug {
        signingConfig signingConfigs.debug
    }
    release {
        signingConfig signingConfigs.release
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

flavorDimensions "flavor-type"

productFlavors {
    dev {
        dimension "flavor-type"
        applicationIdSuffix ".dev"
    }
    preprod {
        dimension "flavor-type"
        buildTypes.release.signingConfig signingConfigs.releasetest
    }
    prod {
        dimension "flavor-type"
    }
}
SaraAkwad commented 1 year ago

I faced the same issue, any solution?

rsanjuan87 commented 1 year ago

It would be useful if some visual mark like a color overlay was added to the component that throws this exception so that it can be visually determined which widget is the one that is defective

RoarGronmo commented 1 year ago

Could "lint" flag this error ?

markrheeGI commented 1 week ago

The main issue here is that because of this bug what seems to work during development in debug mode suddenly fails in production in release mode. The developers have faith in the Flutter framework to at least render what was rendered in debug mode in release mode as well.

lubritto commented 20 hours ago

It seems Flutter is ignoring it on purpose to avoid other rendering issues by adding the ErrorWidget, but honestly, the current behavior is leading to misassumptions given in debug mode everything looks ok on the UI. For sure there might be a better way of handling it.

Screenshot 2024-07-15 at 2 14 26 PM
lubritto commented 19 hours ago

@goderbauer @Hixie Is there another way to handle such scenarios in the current state of the framework? Relying on debuggers to catch it is not safe enough. A lint rule maybe? It would prevent people from causing it but still have a bad error report on debug mode if any edge case happens. A bit more context would allow me to contribute here.