' 'Guru Meditation #48454C50.
$hex', textAlign: TextAlign.
center, style: TextStyle( color: red, fontFamily: 'Courier', fontSize: 16, fontWeight: FontWeight.
bold, inherit: false, ), ), )It starts to look like the real deal (please ignore the notch for now):Let’s add the iconic red border next.
There’s one problem, though.
I don’t want the text to wrap (which would happen on smaller devices).
Furthermore, if you compare the Courier font I used with the original Amiga font, it runs too wide.
So let’s transform the Text widget by scaling it horizontally to 75%.
To keep it at the same width, I have to enlarge the width by 133% at the same time.
The text should now always fit the screen.
Container( alignment: Alignment.
center, decoration: BoxDecoration( color: black, border: Border.
all(color: red, width: 8), ), padding: EdgeInsets.
symmetric( horizontal: 8, vertical: 16, ), child: FractionallySizedBox( widthFactor: 4 / 3, child: Transform( alignment: Alignment.
center, transform: Matrix4.
identity().
scaled(3 / 4, 1), child: Text( .
), ), ), ),We’re almost there…As you probably see on my screen shots, if the device has a notch, the alert doesn’t look right.
I’d like to move the box below the notch but cover everything above in black.
The Amiga also displays a small black border around the red border, so let’s add this, too.
A SafeArea widget inside another Container can do both.
I need to disable its bottom margin, though.
return Positioned( left: 0, right: 0, child: Container( color: black, child: SafeArea( minimum: EdgeInsets.
all(8), bottom: false, child: GestureDetector( .
), ), ), );And there, it is, perfectly aligned:For the final and most important step, I will add blinking.
The following might be a bit hacky, I don’t know, but it works and I don’t have to create a custom StatefulWidget.
I’m using a StatefulBuilder instead.
Each time, that widget asks its builder function to recreate the UI, I setup a timer to toggle the border color from red to black and back again after 700ms.
Then, I’m using an AnimatedContainer to implicitly animate this color change within 300ms.
Some things are just too easy in Flutter.
GestureDetector( onTap: () { blink = null; overlay.
remove(); }, child: StatefulBuilder( builder: (context, setState) { Future.
delayed(Duration(milliseconds: 700)) .
then((_) { if (blink != null) setState(() => blink = !blink); }); return AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.
easeInOut, alignment: Alignment.
center, decoration: BoxDecoration( color: black, border: Border.
all( color: blink ? red : black, width: 8), ), .
); }, ), ),Here is the hacky part: Because Flutter throws an exception if I tap the alert and setState is then called on a disposed widget, I need to protect myself against this case by setting blink to null and explicitly checking for it.
Perhaps, I should have used a Timer instead of a Future because that timer could be cancelled, I think.
The future is inevitable.
Back to the future…And there you have it, an alert box that looks much better than the usual modal dialog.
And at least with old folk like myself, people will have a positive nostalgic feeling instead of cold-blooded anger because your app didn’t work for them as expected.
Here is the source code, by the way.
.. More details