为Flutter应用添加启动画面

问题描述 投票:57回答:11

您如何为Flutter应用添加启动画面?它应该在任何其他内容之前加载和显示。目前,在Scaffold(home:X)小部件加载之前,会有一个短暂的颜色闪烁。

flutter
11个回答
123
投票

我想为Flutter中的Splash屏幕的实际方式带来更多亮点。

我跟踪了一点跟踪here,我发现Flutter中的Splash Screen看起来并不那么糟糕。

也许大多数开发人员(像我一样)都认为Flutter默认没有Splash屏幕,他们需要对此做些什么。实际上有Splash画面,但它是白色背景,没有人能够理解默认情况下已经有iOS和Android的启动画面。

这实际上很酷,因为开发人员唯一需要做的就是将Branding图像放在正确的位置,启动画面就会像这样开始工作。

现在,这是如何做到这一步,一步一步:

首先是Android(因为是我最喜欢的平台:))

  1. 在Flutter项目中找到“android”文件夹。
  2. 浏览到app - > src - > main - > res文件夹,将品牌图像的所有变体放在相应的文件夹中。例如: 密度为1的图像需要放在mipmap-mdpi中, 密度为1.5的图像需要放在mipmap-hdpi中, 密度为2的图像需要放在mipmap-xdpi中, 密度为3的图像需要放在mipmap-xxdpi中, 密度为4的图像需要放在mipmap-xxxdpi中,

默认情况下,android文件夹没有drawable-mdpi,drawable-hdpi等,但如果他愿意,每个人都可以创建。这就是为什么图像需要放在mipmap文件夹中。此外,关于Splash屏幕(在Android中)的默认XML代码将在@mipmap中查看,而不是在@drawable中(如果需要,可以更改它)。

  1. Android的最后一步是取消注释drawable / launch_background.xml中的一些代码。浏览到app - > src - > main - > res-> drawable并打开launch_background.xml。在此文件中,您将看到为什么Slash屏幕背景为白色。要应用我们在步骤2中放置的品牌形象,我们必须取消注释launch_background.xml文件中的一些XML代码,因此在更改后代码应如下所示: <!--<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:

  1. 在Flutter项目中找到“ios”文件夹。
  2. 浏览到Runner - > Assets.xcassets - > LaunchImage.imageset。应该有LaunchImage.png,LaunchImage @ 2x.png等。现在你必须用品牌图像变体替换这些图像。例如: 密度为1的图像需要覆盖LaunchImage.png, 密度为2的图像需要覆盖[email protected], 密度为3的图像需要覆盖[email protected], 密度为4的图像需要覆盖[email protected]

如果我没有错,默认情况下[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上,您应该拥有正确的启动画面,其中包含您添加的品牌形象。

谢谢


1
投票

有多种方法可以做到这一点,但我使用的最简单方法是:

对于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"
    }

1
投票

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


20
投票

如果你flutter created你的项目,你可以按照https://flutter.io/assets-and-images/#updating-the-launch-screen的步骤。


13
投票

目前还没有一个很好的例子,但您可以使用每个平台的本机工具自己完成:

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


7
投票

对于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" />

6
投票

你应该尝试下面的代码,为我工作

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,
      )),
    ),
  );
}
}

4
投票

@Collin Jackson和@Sniper都是对的。您可以按照以下步骤分别在Android和iOS中设置启动映像。然后在您的MyApp()中,在您的initState()中,您可以使用Future.delayed来设置计时器或调用任何api。在从Future返回响应之前,将显示您的启动图标,然后随着响应的到来,您可以在启动屏幕后移动到要转到的屏幕。你可以看到这个链接:Flutter Splash Screen


4
投票

如果在应用经过验证的答案后未找到图像等错误,请确保添加@ mipmap / ic_launcher而不是@mipmap / ic_launcher.png


2
投票

添加如下页面和路由可能会有所帮助

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


1
投票

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,
        )
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.