मैं एक स्पंदन ऐप डीबग करने का प्रयास कर रहा हूं। मैंने देखा कि कुछ मामलों में अपवाद फेंके जाते हैं, लेकिन कंसोल में प्रदर्शित नहीं होते हैं। इसलिए मुझे यह जानने में थोड़ा समय लगा कि कोई अपवाद है। यह बहुत समय बर्बाद होता है।

समस्या दिखाने के लिए यहां एक छोटा कोड स्निपेट है। उठाया बटन के साथ अपवाद दिखाया गया है, लेकिन TextField के साथ नहीं। मुझे अपवाद मुद्रित करने के लिए एक कोशिश/पकड़ जोड़ने के लिए मजबूर होना पड़ता है, अन्यथा यह अदृश्य है।

समस्या स्वयं त्रुटि नहीं है, समस्या यह है कि त्रुटि दिखाई नहीं देती है। कृपया मुझे बताएं कि मैं सभी अपवाद कैसे दिखा सकता हूं।

void main() => runApp(Test());

class Test extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          // EXCEPTION CAUGHT:
          // child: RaisedButton(child: Text('Test'), onPressed: () => throw Exception()),
          // EXCEPTION NOT CAUGHT:
          child: TextField(onSubmitted: (value) => throw Exception()),
        ),
      ),
    );
  }
}
2
Patrick 16 सितंबर 2020, 23:19

2 जवाब

सबसे बढ़िया उत्तर

यह एक दिलचस्प शिकार बनकर समाप्त हुआ।

onSubmitted की विधि TextField (साथ ही TextFormField की onFieldSubmitted विधि) को EditableText की performAction विधि से कहा जाता है। बेस क्लास, जिसे अंततः एक प्लेटफ़ॉर्म चैनल संदेश के परिणाम के रूप में कहा जाता है (विशेष रूप से वह संदेश जो कहता है "मैंने अब इस टेक्स्ट फ़ील्ड को संपादित किया है")। MethodChannel वर्ग के भीतर, इवेंट को विजेट में पास करने के लिए जिम्मेदार तरीका है _handleAsMethodCall विधि:

Future<ByteData> _handleAsMethodCall(ByteData message, Future<dynamic> handler(MethodCall call)) async {
  final MethodCall call = codec.decodeMethodCall(message);
  try {
    return codec.encodeSuccessEnvelope(await handler(call));
  } on PlatformException catch (e) {
    return codec.encodeErrorEnvelope(
      code: e.code,
      message: e.message,
      details: e.details,
    );
  } on MissingPluginException {
    return null;
  } catch (e) {
    return codec.encodeErrorEnvelope(code: 'error', message: e.toString(), details: null);
  }
}

जैसा कि आप देख सकते हैं, विधि कॉल को एक कोशिश/पकड़ में लपेटा जाता है जो त्रुटियों को अवशोषित करता है और त्रुटि को स्वाभाविक रूप से बबल करने की अनुमति देने के बजाय त्रुटि प्रतिक्रियाओं के रूप में प्लेटफ़ॉर्म पर लौटाता है। इसका परिणाम यह होता है कि त्रुटि संदेश को आंतरिक BinaryMessenger द्वारा नियंत्रित किया जाता है, न कि स्वयं डार्ट द्वारा। वहां से यह Window._respondToPlatformMessageपर समाप्त होता है। विधि, जो एक देशी-बाध्य विधि और एक खोजी डेड-एंड है (जब तक कि आपको फ़्लटर के प्लेटफ़ॉर्म-विशिष्ट मूल कार्यान्वयन का ज्ञान न हो)।

मुझे यकीन नहीं है कि यह इरादा व्यवहार या बग है, लेकिन किसी भी मामले में, परिणाम यह प्रतीत होता है कि प्लेटफ़ॉर्म/विधि चैनल सिस्टम द्वारा त्रुटि अवशोषित हो जाती है। मैं फ़्लटर गिटहब पेज पर इसके लिए एक समस्या तैयार कर रहा हूं, और जब समस्या पोस्ट की जाती है तो इस उत्तर को लिंक के साथ अपडेट कर देगा।

संपादित करें: समस्या पृष्ठ यहां है।

2
Abion47 17 सितंबर 2020, 11:06

आप चाहें तो फ़्लटर रेपो में इसे एक समस्या के रूप में दर्ज कर सकते हैं

यह कैसे काम करता है, इसकी एक बुनियादी व्याख्या यहां दी गई है।

यदि आप स्रोत कोड को देखते हैं, तो RaisedButton.onPressed को InkWell.onTap को सौंप दिया जाता है, जो तब एक InkResponse.onTap पर जाता है, जो इसे अंतिम पंक्ति में बुलाता है:

  void _handleTap(BuildContext context) {
    _currentSplash?.confirm();
    _currentSplash = null;
    updateHighlight(_HighlightType.pressed, value: false);
    if (widget.onTap != null) {
      if (widget.enableFeedback)
        Feedback.forTap(context);
      widget.onTap();
    }
  }

यह GestureRecognizer द्वारा एक invokeCallback में लिपटा हुआ है जो एक try-catch करता है और त्रुटि को एक FlutterError में बदल देता है जिसे आप अपने कंसोल पर अच्छी तरह से स्वरूपित देखते हैं।

T invokeCallback<T>(String name, RecognizerCallback<T> callback, { String debugReport() }) {
    assert(callback != null);
    T result;
    try {
      assert(() {
        if (debugPrintRecognizerCallbacksTrace) {
          final String report = debugReport != null ? debugReport() : null;
          // The 19 in the line below is the width of the prefix used by
          // _debugLogDiagnostic in arena.dart.
          final String prefix = debugPrintGestureArenaDiagnostics ? ' ' * 19 + '❙ ' : '';
          debugPrint('$prefix$this calling $name callback.${ report?.isNotEmpty == true ? " $report" : "" }');
        }
        return true;
      }());
      result = callback();
    } catch (exception, stack) {
      InformationCollector collector;
      assert(() {
        collector = () sync* {
          yield StringProperty('Handler', name);
          yield DiagnosticsProperty<GestureRecognizer>('Recognizer', this, style: DiagnosticsTreeStyle.errorProperty);
        };
        return true;
      }());
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'gesture',
        context: ErrorDescription('while handling a gesture'),
        informationCollector: collector
      ));
    }
    return result;
  }

दूसरी ओर TextField.onSubmitted को EditableText.onSubmitted में पास किया जाता है और इसे अंतिम पंक्ति में कहा जाता है:

void _finalizeEditing(bool shouldUnfocus) {
    // Take any actions necessary now that the user has completed editing.
    if (widget.onEditingComplete != null) {
      widget.onEditingComplete();
    } else {
      // Default behavior if the developer did not provide an
      // onEditingComplete callback: Finalize editing and remove focus.
      widget.controller.clearComposing();
      if (shouldUnfocus)
        widget.focusNode.unfocus();
    }

    // Invoke optional callback with the user's submitted content.
    if (widget.onSubmitted != null)
      widget.onSubmitted(_value.text);
  }

यह एक MethodChannel द्वारा लपेटा गया है:

Future<ByteData> _handleAsMethodCall(ByteData message, Future<dynamic> handler(MethodCall call)) async {
    final MethodCall call = codec.decodeMethodCall(message);
    try {
      return codec.encodeSuccessEnvelope(await handler(call));
    } on PlatformException catch (e) {
      return codec.encodeErrorEnvelope(
        code: e.code,
        message: e.message,
        details: e.details,
      );
    } on MissingPluginException {
      return null;
    } catch (e) {
      return codec.encodeErrorEnvelope(code: 'error', message: e.toString(), details: null);
    }
  }

और वह अंतिम catch (e) सक्रिय हो जाता है और फिर आपको कोई त्रुटि दिखाई नहीं देती है।

मुझे संदेह है कि यदि आप रिलीज करने के लिए अपने ऐप को संकलित करते हैं तो आप इसे देखेंगे, क्योंकि डार्ट वीएम को एसिंक्रोनस कोड के लिए डीबग मोड में अपवाद देने में समस्या है (मैं यहां चीजों को सरल बना रहा हूं)।

1
Michel Feinstein 17 सितंबर 2020, 01:29