Last active
March 21, 2024 10:55
-
-
Save KDCinfo/ba5d7209197f6c94231753e42baf36c6 to your computer and use it in GitHub Desktop.
iOS Unusual App Crashing - Flutter 3.21
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:developer'; | |
import 'package:flutter/material.dart'; | |
/// To recreate: | |
/// | |
/// 1. Using any Flutter version merged after Mar 7, 2024, (e.g. v3.21), | |
/// build and run the provided code example on an iOS device or simulator. | |
/// 2. Tap the title in the AppBar. | |
/// | |
/// Note: If code was changed and hot-refreshed, the first tap could be | |
/// buffered, and the 2nd tap may cause the crash (if expected). | |
/// Tapping two or more times for testing is recommended | |
/// (unless it crashes on the first tap, obv.) | |
/// | |
/// 3. Observe the app: | |
/// 3a. Crashes (iPad simulator) with the console message, | |
/// "Lost connection to device.", or, | |
/// 3b. Hits a raster-level breakpoint in Xcode (physical iPad). | |
/// | |
/// - Anecdotally, an iPad simulator crashed, while a physical iPad | |
/// popped up a breakpoint in Xcode (despite running in VS Code). YMMV | |
/// | |
/// - When the physical device hit the breakpoint in Xcode, I 'continued' | |
/// the step-through over 60 times, and eventually just stopped the runner. | |
/// | |
/// - Other than the one-line console output after the | |
/// crash on the simulator, no logs were dumped in either case. | |
/// | |
/// Expected result: Tapping the title in the AppBar one or more times, | |
/// the console log should output one line per tap, | |
/// and the app should not crash. | |
/// | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
debugShowCheckedModeBanner: false, | |
title: AppConfig.appTitle, | |
theme: ThemeData( | |
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), | |
useMaterial3: true, | |
), | |
home: const MyHomePage(), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
const MyHomePage({super.key}); | |
@override | |
State<MyHomePage> createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
@override | |
Widget build(BuildContext context) { | |
return SafeArea( | |
child: Scaffold( | |
appBar: AppBar( | |
backgroundColor: Colors.amberAccent, | |
title: Center( | |
child: ColoredBox( | |
color: Colors.lightBlueAccent, | |
child: InkWell( | |
onTap: () { | |
log('[main.dart] InkWell onTap'); | |
}, | |
child: const Padding( | |
padding: EdgeInsets.symmetric( | |
horizontal: 20, | |
vertical: 10, | |
), | |
child: Text( | |
AppConfig.appBarTitleTap, | |
textAlign: TextAlign.center, | |
style: TextStyle( | |
fontSize: 22, | |
color: Colors.black, | |
fontWeight: FontWeight.bold, | |
), | |
), | |
), | |
), | |
), | |
), | |
), | |
body: Align( | |
alignment: Alignment.topCenter, | |
child: SizedBox( | |
width: 400, | |
height: 400, | |
/// In the comments below, "it" means tapping the AppBar title. | |
/// | |
/// The code as-is will crash the app on iOS when the AppBar | |
/// title is tapped, until one of the options below are changed. | |
/// | |
/// Removing this `SingleChildScrollView` will make it work. [#1] | |
/// (Not the column.) | |
child: SingleChildScrollView( | |
child: Column( | |
children: [ | |
/// | |
/// After removing the `SingleChildScrollView` above, | |
/// which will make it work, removing this `Padding` | |
/// will then make it crash. [#1b] | |
const Padding( | |
padding: EdgeInsets.only(top: 20), | |
child: Text( | |
AppConfig.appDesc, | |
style: TextStyle( | |
fontSize: 22, | |
), | |
), | |
), | |
/// This is the outside wrapper `Card`. | |
Card( | |
/// Reverting the changes above (resetting it to crash), | |
/// commenting out this property will work. [#2] | |
/// For [#2] and [#3] it would seem you can't have | |
/// a nested `Card`, each with its own clipBehavior. | |
clipBehavior: Clip.hardEdge, | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(30), | |
), | |
color: Colors.orangeAccent, | |
elevation: AppConfig.cardElevationWrapper, | |
margin: const EdgeInsets.symmetric(vertical: 30), | |
/// A wrapper Form for the TextFormField. | |
child: Form( | |
key: AppConfig.formKey, | |
/// | |
/// This is the inside `Card`. | |
child: Card( | |
/// | |
/// Commenting out this property will work. [#3] | |
clipBehavior: Clip.antiAlias, | |
/// Commenting out this property will work. [#4] | |
/// Also, copying the `shape` above will work [#4b]. | |
shape: const RoundedRectangleBorder( | |
borderRadius: BorderRadius.only( | |
bottomLeft: Radius.circular(20), | |
bottomRight: Radius.circular(20), | |
), | |
), | |
color: Colors.lightGreenAccent, | |
elevation: AppConfig.cardElevationInside, | |
margin: const EdgeInsets.symmetric( | |
horizontal: AppConfig.spacingHorizontal, | |
vertical: AppConfig.spacingVertical, | |
), | |
/// Changing this to a Text widget will work. [#5] | |
/// A FormField may also crash the app (tried once). | |
child: TextFormField( | |
decoration: const InputDecoration( | |
hintText: AppConfig.textFormFieldInitialValue, | |
border: InputBorder.none, | |
contentPadding: EdgeInsets.symmetric( | |
horizontal: AppConfig.spacingHorizontal, | |
vertical: AppConfig.spacingVertical, | |
), | |
), | |
), | |
// child: Text( | |
// AppConfig.textFormFieldInitialValue, | |
// style: TextStyle( | |
// fontSize: 22, | |
// ), | |
// ), | |
), | |
), | |
), | |
], | |
), | |
), | |
), | |
), | |
), | |
); | |
} | |
} | |
abstract class AppConfig { | |
static final GlobalKey<FormState> formKey = GlobalKey<FormState>(); | |
static const String appTitle = 'Debugging an iOS Crash'; | |
static const String appDesc = 'Debugging an unusual iOS Crash with various widgets'; | |
static const String appBarTitleTap = '[ Tap to Crash (or log) ]'; | |
static const String textFormFieldInitialValue = 'TextFormField or FormField'; | |
static const double cardElevationWrapper = 5; | |
static const double cardElevationInside = 10; | |
static const double spacingHorizontal = 20; | |
static const double spacingVertical = 10; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment