您如何为Flutter应用添加启动画面?它应该在任何其他内容之前加载和显示。目前,在Scaffold(home:X)小部件加载之前,会有一个短暂的颜色闪烁。
我想为Flutter中的Splash屏幕的实际方式带来更多亮点。
我跟踪了一点跟踪here,我发现Flutter中的Splash Screen看起来并不那么糟糕。
也许大多数开发人员(像我一样)都认为Flutter默认没有Splash屏幕,他们需要对此做些什么。实际上有Splash画面,但它是白色背景,没有人能够理解默认情况下已经有iOS和Android的启动画面。
这实际上很酷,因为开发人员唯一需要做的就是将Branding图像放在正确的位置,启动画面就会像这样开始工作。
现在,这是如何做到这一步,一步一步:
首先是Android(因为是我最喜欢的平台:))
默认情况下,android文件夹没有drawable-mdpi,drawable-hdpi等,但如果他愿意,每个人都可以创建。这就是为什么图像需要放在mipmap文件夹中。此外,关于Splash屏幕(在Android中)的默认XML代码将在@mipmap中查看,而不是在@drawable中(如果需要,可以更改它)。
<!--<item android:drawable="@android:color/white" />-->
<item>
<bitmap
android:gravity="center"
android:src="@mipmap/your_image_name" />
</item>
请注意我们评论白色背景代码并取消注释有关mipmap图像的代码。如果有人感兴趣,则在styles.xml文件中使用此launch_background.xml。
现在适用于iOS:
如果我没有错,默认情况下[email protected]不存在,但您可以轻松创建一个。如果[email protected]不存在,您还必须在Contents.json文件中声明它,它与图像位于同一目录中。更改后,我的Contents.json文件如下所示:
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "[email protected]",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "[email protected]",
"scale" : "3x"
},
{
"idiom" : "universal",
"filename" : "[email protected]",
"scale" : "4x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
这应该是全部。下次运行应用程序时,在Android和iOS上,您应该拥有正确的启动画面,其中包含您添加的品牌形象。
谢谢
有多种方法可以做到这一点,但我使用的最简单方法是:
对于Launch Icons,我使用了颤动库Flutter Launcher Icon
对于自定义启动画面,我创建不同的屏幕分辨率,然后根据Android的分辨率在mipmap文件夹中添加启动图像。
最后一部分是调整Android中res文件夹中drawable文件夹中的launch_background.xml。
只需将代码更改为如下所示:
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- <item android:drawable="@android:color/white" />
<item android:drawable="@drawable/<splashfilename>" /> --> -->
<!-- You can insert your own image assets here -->
<item>
<bitmap
android:gravity="center"
android:src="@mipmap/<Your splash image name here as per the mipmap folder>"/>
</item>
</layer-list>
我见过很少的开发人员将这些启动添加为drawable,我尝试了这个,但不知何故,在Flutter 1.0.0和Dart SDK 2.0+中构建失败了。因此,我更喜欢在位图部分添加启动。
iOS Splash-screen创建相当简单。
在iOS的Runner文件夹中,只需使用与LaunchImage.png @ 2x,@ 3x,@ 4x同名的自定义Splash屏幕图像更新LaunchImage.png文件。
只是一个补充,我觉得在LaunchImage.imageset中有一个4x图像也很好。只需使用以下行更新Content.json中的代码,低于3倍的比例即可添加4倍缩放选项:
{
"idiom" : "universal",
"filename" : "[email protected]",
"scale" : "4x"
}
Flutter实际上提供了一种更简单的方法来为我们的应用程序添加Splash Screen。我们首先需要在设计其他应用程序屏幕时设计基本页面。您需要将其设置为StatefulWidget,因为状态会在几秒钟内发生变化。
import 'dart:async';
import 'package:flutter/material.dart';
import 'home.dart';
class SplashScreen extends StatefulWidget {
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
super.initState();
Timer(
Duration(seconds: 3),
() => Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => HomeScreen())));
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: Image.asset('assets/splash.png'),
),
);
}
}
逻辑在initState()内部,按照你的意愿调用持续时间的Timer(),我做了3秒,一旦完成,将导航器推到我们应用程序的主屏幕。
注意:应用程序应该只显示一个启动画面,用户不应该在后退按钮上再次返回它。为此,我们使用Navigator.pushReplacement(),它将移动到新屏幕并从导航历史堆栈中删除上一个屏幕。
为了更好地理解,请访问Flutter: Design your own Splash Screen
如果你flutter create
d你的项目,你可以按照https://flutter.io/assets-and-images/#updating-the-launch-screen的步骤。
目前还没有一个很好的例子,但您可以使用每个平台的本机工具自己完成:
iOS:https://docs.nativescript.org/publishing/creating-launch-screens-ios
Android:https://www.bignerdranch.com/blog/splash-screens-the-right-way/
订阅issue 8147以获取有关启动画面的示例代码的更新。如果启动画面和iOS上的应用程序之间的黑色闪烁困扰您,请订阅issue 8127以获取更新。
编辑:截至2017年8月31日,新项目模板中现在提供了对闪屏的改进支持。见#11505。
对于Android,请转到android> app> src> main> res> drawable> launcher_background.xml
现在取消注释并将@ mipmap / launch_image替换为您的图像位置。
<item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item>
您可以在此处更改屏幕颜色 -
<item android:drawable="@android:color/white" />
你应该尝试下面的代码,为我工作
import 'dart:async';
import 'package:attendance/components/appbar.dart';
import 'package:attendance/homepage.dart';
import 'package:flutter/material.dart';
class _SplashScreenState extends State<SplashScreen>
with SingleTickerProviderStateMixin {
void handleTimeout() {
Navigator.of(context).pushReplacement(new MaterialPageRoute(
builder: (BuildContext context) => new MyHomePage()));
}
startTimeout() async {
var duration = const Duration(seconds: 3);
return new Timer(duration, handleTimeout);
}
@override
void initState() {
// TODO: implement initState
super.initState();
_iconAnimationController = new AnimationController(
vsync: this, duration: new Duration(milliseconds: 2000));
_iconAnimation = new CurvedAnimation(
parent: _iconAnimationController, curve: Curves.easeIn);
_iconAnimation.addListener(() => this.setState(() {}));
_iconAnimationController.forward();
startTimeout();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Scaffold(
body: new Center(
child: new Image(
image: new AssetImage("images/logo.png"),
width: _iconAnimation.value * 100,
height: _iconAnimation.value * 100,
)),
),
);
}
}
@Collin Jackson和@Sniper都是对的。您可以按照以下步骤分别在Android和iOS中设置启动映像。然后在您的MyApp()中,在您的initState()中,您可以使用Future.delayed来设置计时器或调用任何api。在从Future返回响应之前,将显示您的启动图标,然后随着响应的到来,您可以在启动屏幕后移动到要转到的屏幕。你可以看到这个链接:Flutter Splash Screen
如果在应用经过验证的答案后未找到图像等错误,请确保添加@ mipmap / ic_launcher而不是@mipmap / ic_launcher.png
添加如下页面和路由可能会有所帮助
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutkart/utils/flutkart.dart';
import 'package:flutkart/utils/my_navigator.dart';
class SplashScreen extends StatefulWidget {
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
// TODO: implement initState
super.initState();
Timer(Duration(seconds: 5), () => MyNavigator.goToIntro(context));
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
decoration: BoxDecoration(color: Colors.redAccent),
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 2,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircleAvatar(
backgroundColor: Colors.white,
radius: 50.0,
child: Icon(
Icons.shopping_cart,
color: Colors.greenAccent,
size: 50.0,
),
),
Padding(
padding: EdgeInsets.only(top: 10.0),
),
Text(
Flutkart.name,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 24.0),
)
],
),
),
),
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
Padding(
padding: EdgeInsets.only(top: 20.0),
),
Text(
Flutkart.store,
softWrap: true,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Colors.white),
)
],
),
)
],
)
],
),
);
}
}
如果您想要了解,请参阅:https://www.youtube.com/watch?v=FNBuo-7zg2Q
Jaldhi Bhatt的代码对我不起作用。
颤抖投掷'Navigator operation requested with a context that does not include a Navigator'。
我修复了将Navigator使用者组件包装在另一个组件内的代码,该组件使用路由初始化Navigator上下文,如this文章中所述。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:my-app/view/main-view.dart';
class SplashView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: Builder(
builder: (context) => new _SplashContent(),
),
routes: <String, WidgetBuilder>{
'/main': (BuildContext context) => new MainView()}
);
}
}
class _SplashContent extends StatefulWidget{
@override
_SplashContentState createState() => new _SplashContentState();
}
class _SplashContentState extends State<_SplashContent>
with SingleTickerProviderStateMixin {
var _iconAnimationController;
var _iconAnimation;
startTimeout() async {
var duration = const Duration(seconds: 3);
return new Timer(duration, handleTimeout);
}
void handleTimeout() {
Navigator.pushReplacementNamed(context, "/main");
}
@override
void initState() {
super.initState();
_iconAnimationController = new AnimationController(
vsync: this, duration: new Duration(milliseconds: 2000));
_iconAnimation = new CurvedAnimation(
parent: _iconAnimationController, curve: Curves.easeIn);
_iconAnimation.addListener(() => this.setState(() {}));
_iconAnimationController.forward();
startTimeout();
}
@override
Widget build(BuildContext context) {
return new Center(
child: new Image(
image: new AssetImage("images/logo.png"),
width: _iconAnimation.value * 100,
height: _iconAnimation.value * 100,
)
);
}
}