From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 2608B6B643 for ; Fri, 11 Dec 2020 11:37:21 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 234B51EE45 for ; Fri, 11 Dec 2020 11:37:21 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id C7D671EE36 for ; Fri, 11 Dec 2020 11:37:13 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 920274431C for ; Fri, 11 Dec 2020 11:37:13 +0100 (CET) From: Aaron Lauterer To: pve-devel@lists.proxmox.com Date: Fri, 11 Dec 2020 11:37:08 +0100 Message-Id: <20201211103708.32431-4-a.lauterer@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201211103708.32431-1-a.lauterer@proxmox.com> References: <20201211103708.32431-1-a.lauterer@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.005 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record X-Mailman-Approved-At: Fri, 11 Dec 2020 12:13:14 +0100 Subject: [pve-devel] [PATCH pve_flutter_frontend v3 3/3] Add first welcome screen X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 11 Dec 2020 10:37:21 -0000 Signed-off-by: Aaron Lauterer --- passing through the page controller to the DotIndicator class is necessary as it needs to be a listenable. v2 -> v3: Worked on the overall layout of the welcome screen to look good on screens on all sizes. Limited the max width and made sure that on really small screens or in landscape mode, scrolling is possible. Made sure the logo on the first page does not get too big when in landscape mode. v1 -> v2: * change welcome screens * no color change * buttons instead of text links * use async where needed * code cleanup in several places * use SingleChildScrollView for content heavy welcome screens .../ssl_validate/login_manager_screen.png | Bin 0 -> 20389 bytes .../login_manager_screen_settings.png | Bin 0 -> 37362 bytes lib/main.dart | 14 +- lib/utils/dot_indicator.dart | 108 +++++++++++ .../pve_welcome_common.dart | 68 +++++++ .../firstWelcomeScreen/pve_welcome_faq.dart | 59 ++++++ .../firstWelcomeScreen/pve_welcome_last.dart | 94 ++++++++++ .../firstWelcomeScreen/pve_welcome_logo.dart | 49 +++++ .../pve_welcome_ssl_hint.dart | 50 +++++ lib/widgets/pve_first_welcome_screen.dart | 173 ++++++++++++++++++ pubspec.yaml | 1 + 11 files changed, 615 insertions(+), 1 deletion(-) create mode 100644 assets/images/ssl_validate/login_manager_screen.png create mode 100644 assets/images/ssl_validate/login_manager_screen_settings.png create mode 100644 lib/utils/dot_indicator.dart create mode 100644 lib/widgets/firstWelcomeScreen/pve_welcome_common.dart create mode 100644 lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart create mode 100644 lib/widgets/firstWelcomeScreen/pve_welcome_last.dart create mode 100644 lib/widgets/firstWelcomeScreen/pve_welcome_logo.dart create mode 100644 lib/widgets/firstWelcomeScreen/pve_welcome_ssl_hint.dart create mode 100644 lib/widgets/pve_first_welcome_screen.dart diff --git a/assets/images/ssl_validate/login_manager_screen.png b/assets/images/ssl_validate/login_manager_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..b6668cc8f5b3fcc742ee2a6ce019ea4318fda92a GIT binary patch literal 20389 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 Wyv;~hkMo}ZKjI=X!X<*AzWyH_xa}4I literal 0 HcmV?d00001 diff --git a/lib/main.dart b/lib/main.dart index 8cd6c36..57ad39c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:pve_flutter_frontend/widgets/pve_first_welcome_screen.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:proxmox_login_manager/proxmox_login_manager.dart'; import 'package:pve_flutter_frontend/bloc/pve_authentication_bloc.dart'; import 'package:pve_flutter_frontend/bloc/pve_cluster_status_bloc.dart'; @@ -47,6 +49,7 @@ void main() async { FlutterError.dumpErrorToConsole(details); if (kReleaseMode) ProxmoxGlobalErrorBloc().addError(details.exception); }; + SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); Provider.debugCheckInvalidValueType = null; runApp( @@ -60,6 +63,7 @@ void main() async { ], child: MyApp( authbloc: authBloc, + sharedPreferences: sharedPreferences, ), ), ); @@ -67,9 +71,12 @@ void main() async { class MyApp extends StatelessWidget { final PveAuthenticationBloc authbloc; + final SharedPreferences sharedPreferences; final GlobalKey navigatorKey = GlobalKey(); - MyApp({Key key, this.authbloc}) : super(key: key); + MyApp({Key key, this.authbloc, this.sharedPreferences}) + : assert(sharedPreferences != null), + super(key: key); @override Widget build(BuildContext context) { @@ -142,6 +149,11 @@ class MyApp extends StatelessWidget { builder: (context) => PveSplashScreen(), ); } + if (sharedPreferences.getBool('showWelcomeScreen') ?? true) { + return MaterialPageRoute( + builder: (context) => PveWelcome(), + ); + } if (authbloc.state.value is Unauthenticated || context.name == '/login') { diff --git a/lib/utils/dot_indicator.dart b/lib/utils/dot_indicator.dart new file mode 100644 index 0000000..e4d3027 --- /dev/null +++ b/lib/utils/dot_indicator.dart @@ -0,0 +1,108 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'dart:math'; + +class Dot extends StatelessWidget { + final double dotSpacing; + final double dotSize; + final double zoom; + final double shadowBlurRadius; + final double shadowSpreadRadius; + final Color color; + final int index; + final void Function(int index) onTap; + + const Dot({ + Key key, + this.dotSpacing, + this.dotSize, + this.zoom, + this.shadowBlurRadius, + this.shadowSpreadRadius, + this.color, + this.index, + this.onTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: dotSpacing, + child: Center( + child: Container( + width: dotSize * zoom, + height: dotSize * zoom, + child: GestureDetector( + onTap: () => onTap(index), + ), + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.white.withOpacity(0.72), + blurRadius: shadowBlurRadius, + spreadRadius: shadowSpreadRadius, + offset: Offset(0.0, 0.0)) + ], + ), + ), + ), + ); + } +} + +class DotIndicator extends AnimatedWidget { + DotIndicator({ + this.controller, + this.itemCount, + this.onPageSelected, + this.color: Colors.white, + this.page, + this.initialPage, + }) : super(listenable: controller); + + final PageController controller; + final double page; + final double initialPage; + + final int itemCount; + + final ValueChanged onPageSelected; + final Color color; + + static const double _dotSize = 8.0; + static const double _maxZoom = 1.2; + static const double _dotSpacing = 25.0; + + Widget _buildDot(int index) { + double selectedness = Curves.easeOut.transform( + max( + 0.0, + 1.0 - ((controller.page ?? controller.initialPage) - index).abs(), + ), + ); + double zoom = 1.0 + (_maxZoom - 1.0) * selectedness; + double shadowBlurRadius = 4.0 * selectedness; + double shadowSpreadRadius = 1.0 * selectedness; + return new Dot( + color: color, + shadowBlurRadius: shadowBlurRadius, + shadowSpreadRadius: shadowSpreadRadius, + dotSize: _dotSize, + dotSpacing: _dotSpacing, + zoom: zoom, + index: index, + onTap: onPageSelected, + ); + } + + Widget build(BuildContext contect) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate( + itemCount, + _buildDot, + )); + } +} diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_common.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_common.dart new file mode 100644 index 0000000..e0e891d --- /dev/null +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_common.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; + +class PveQuestion extends StatelessWidget { + final String text; + + const PveQuestion({ + Key key, + this.text, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.fromLTRB(10.0, 10.0, 5.0, 0.0), + child: Text( + text, + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ); + } +} + +class PveAnswer extends StatelessWidget { + final String text; + final List spans; + + const PveAnswer({ + Key key, + this.text, + this.spans, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.fromLTRB(20.0, 5.0, 5.0, 5.0), + child: RichText( + text: TextSpan( + text: text, + style: DefaultTextStyle.of(context).style, + children: spans, + ), + ), + ); + } +} + +class PveWelcomePageContent extends StatelessWidget { + final Widget child; + const PveWelcomePageContent({ + Key key, + this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Center( + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.fromLTRB(15.0, 15.0, 15.0, 0.0), + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 500), child: child), + )), + ); + } +} diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart new file mode 100644 index 0000000..6612819 --- /dev/null +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_common.dart'; +import 'package:pve_flutter_frontend/utils/promox_colors.dart'; + +// FAQ +class PveWelcomePageFAQ extends StatelessWidget { + const PveWelcomePageFAQ({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return PveWelcomePageContent( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PveQuestion( + text: + "How do I connect if I am not using the default port 8006?"), + PveAnswer( + text: + "Add the port at the end, separated by a colon. For the default https port add 443."), + PveAnswer( + text: "For example: 192.168.1.10", + spans: [ + TextSpan( + text: ":443", + style: TextStyle( + fontWeight: FontWeight.bold, fontStyle: FontStyle.italic)) + ], + ), + PveQuestion( + text: "What about remote consoles?", + ), + PveAnswer( + text: + "Spice is currently supported. We plan to integrate VNC in the future."), + PveQuestion(text: "Which Spice client works?"), + PveAnswer( + text: + 'Currently only the following 3rd party Spice client works:'), + Center( + child: OutlineButton( + onPressed: () => { + launch( + 'https://play.google.com/store/apps/details?id=com.undatech.opaque') + }, + child: Text('Opague'), + borderSide: BorderSide(color: ProxmoxColors.supportGrey), + textColor: Colors.white, + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_last.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_last.dart new file mode 100644 index 0000000..52fe2b3 --- /dev/null +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_last.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:pve_flutter_frontend/utils/promox_colors.dart'; + +// goodbye +class PveWelcomePageLast extends StatelessWidget { + const PveWelcomePageLast({Key key, this.onDone}) : super(key: key); + + final VoidCallback onDone; + + @override + Widget build(BuildContext context) { + return Container(child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints viewportConstraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: viewportConstraints.maxHeight), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.fromLTRB(15.0, 15.0, 15.0, 0.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Spacer(flex: 3), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("Enjoy the app"), + Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.emoji_people_rounded, + color: Colors.white, + size: 70, + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: RaisedButton( + onPressed: () => {onDone()}, + color: ProxmoxColors.orange, + textColor: Colors.white, + child: Text("Start"), + ), + ), + ], + ), + Spacer(flex: 1), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 500), + child: Column( + children: [ + Text( + 'If you have suggestions or experience any problems, please contact us via', + textAlign: TextAlign.center, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + OutlineButton( + onPressed: () => + {launch('https://forum.proxmox.com')}, + child: Text('Forum'), + borderSide: + BorderSide(color: ProxmoxColors.supportGrey), + textColor: Colors.white, + ), + OutlineButton( + onPressed: () => { + launch( + 'https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-user') + }, + child: Text('User Mailing List'), + borderSide: + BorderSide(color: ProxmoxColors.supportGrey), + textColor: Colors.white, + ), + ], + ), + ], + ), + ), + Spacer( + flex: 2, + ) + ], + ), + ), + ), + ), + ); + })); + } +} diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_logo.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_logo.dart new file mode 100644 index 0000000..8120d8b --- /dev/null +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_logo.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; + +// Big Logo +class PveWelcomePageLogo extends StatelessWidget { + const PveWelcomePageLogo({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container(child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints viewportConstraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: viewportConstraints.maxHeight), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: + BoxConstraints(maxWidth: 160, maxHeight: 180), + child: Padding( + padding: + const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 30.0), + child: Image.asset( + 'assets/images/Proxmox-logo-symbol-white-orange.png', + alignment: Alignment.center, + ), + ), + ), + FittedBox( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Proxmox Virtual Environment', + style: TextStyle(fontSize: 26), + ), + ), + ), + ]), + )))); + })); + } +} diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_ssl_hint.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_ssl_hint.dart new file mode 100644 index 0000000..380c772 --- /dev/null +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_ssl_hint.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_common.dart'; + +// disable ssl validation hint +class PveWelcomePageSSLValidation extends StatelessWidget { + const PveWelcomePageSSLValidation({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return PveWelcomePageContent( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PveQuestion( + text: "Are you using a self signed certificate?", + ), + PveAnswer( + text: "Disable SSL Validation in the Login Manager settings."), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.white, width: 0.5)), + child: Image.asset( + 'assets/images/ssl_validate/login_manager_screen.png', + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.white, width: 0.5)), + child: Image.asset( + 'assets/images/ssl_validate/login_manager_screen_settings.png', + ), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/widgets/pve_first_welcome_screen.dart b/lib/widgets/pve_first_welcome_screen.dart new file mode 100644 index 0000000..27c8853 --- /dev/null +++ b/lib/widgets/pve_first_welcome_screen.dart @@ -0,0 +1,173 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:pve_flutter_frontend/utils/dot_indicator.dart'; +import 'package:pve_flutter_frontend/utils/promox_colors.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_logo.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_faq.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_ssl_hint.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_last.dart'; + +class PveWelcome extends StatefulWidget { + @override + _PveWelcomeState createState() => _PveWelcomeState(); +} + +class _PveWelcomeState extends State with TickerProviderStateMixin { + PageController _controller; + SharedPreferences _sharedPreferences; + + final List _pages = [ + PveWelcomePageLogo(), + PveWelcomePageSSLValidation(), + PveWelcomePageFAQ(), + ]; + + static const Duration _pageChangeDuration = Duration(milliseconds: 150); + static const Curve _pageChangeCurve = Curves.easeInOut; + + bool _isLast = false; + bool _isFirst = true; + + final _buttonTextColor = Colors.white; + final _buttonDisabledTextColor = Colors.white30; + + Future _getPrefs() async { + _sharedPreferences = await SharedPreferences.getInstance(); + } + + Future _prefs; + + @override + void initState() { + super.initState(); + + _prefs = _getPrefs(); + _controller = PageController(); + + // add last page here so we can define the callback for the start button + _pages.add(PveWelcomePageLast(onDone: () { + skipDone(); + })); + + _controller.addListener(() { + setState(() { + _isLast = _controller.page.floor() == _pages.length - 1; + _isFirst = _controller.page.floor() == 0; + }); + }); + } + + Future skipDone() async { + await _sharedPreferences.setBool('showWelcomeScreen', false); + Navigator.pushReplacementNamed(context, '/'); + } + + Widget nextDoneButton() { + if (_isLast) { + return FlatButton( + textColor: _buttonTextColor, + disabledTextColor: _buttonDisabledTextColor, + child: Text( + "Done", + ), + onPressed: () { + skipDone(); + }, + ); + } else { + return FlatButton( + child: Text("Next"), + textColor: _buttonTextColor, + disabledTextColor: _buttonDisabledTextColor, + onPressed: () { + _controller.nextPage( + duration: _pageChangeDuration, curve: _pageChangeCurve); + }, + ); + } + } + + Widget skipPrevButton() { + if (_isFirst) { + return FlatButton( + textColor: _buttonTextColor, + disabledTextColor: _buttonDisabledTextColor, + onPressed: () { + skipDone(); + }, + child: Text( + 'Skip', + ), + ); + } else { + return FlatButton( + textColor: _buttonTextColor, + disabledTextColor: _buttonDisabledTextColor, + child: Text( + "Prev", + ), + onPressed: () { + _controller.previousPage( + duration: _pageChangeDuration, curve: _pageChangeCurve); + }, + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: ProxmoxColors.supportBlue, + body: DefaultTextStyle( + style: TextStyle(color: Colors.white, fontSize: 18), + child: FutureBuilder( + future: _prefs, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return SafeArea( + child: Column( + children: [ + Expanded( + child: PageView.builder( + controller: _controller, + itemCount: _pages.length, + itemBuilder: (context, index) { + return _pages[index]; + }, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + skipPrevButton(), + DotIndicator( + controller: _controller, + itemCount: _pages.length, + onPageSelected: (int page) { + _controller.animateToPage(page, + duration: _pageChangeDuration, + curve: _pageChangeCurve); + }, + ), + nextDoneButton(), + ], + ), + ], + ), + ); + } else { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white70), + ), + ); + } + }, + ), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index cb5bcdf..a605eeb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -61,6 +61,7 @@ flutter: - assets/images/Proxmox_logo_white_orange_800.png - assets/images/Proxmox-logo-symbol-white-orange.png - assets/images/proxmox_logo_icon_white.png + - assets/images/ssl_validate/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. -- 2.20.1