Flutter应用启动流程分析(二)

Flutter应用启动流程分析(二)

看看Flutter页面是怎么展示出来的
MainActivity
在AndroidManifest中的声明

<activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

只是声明了一个meta-data,指定是否展示加载画面,也就是应用打开时的等待画面。接着看MainActivity的代码实现

class MainActivity: FlutterActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)
  }
}

乖乖,继承了FlutterActivity,复写了onCreate方法就添加了一行代码,看名称貌似是注册了什么,进去瞅瞅,嘟嘟嘟…

/**
 * Generated file. Do not edit.
 */
public final class GeneratedPluginRegistrant {
  public static void registerWith(PluginRegistry registry) {
    if (alreadyRegisteredWith(registry)) {
      return;
    }
  }

  private static boolean alreadyRegisteredWith(PluginRegistry registry) {
    final String key = GeneratedPluginRegistrant.class.getCanonicalName();
    if (registry.hasPlugin(key)) {
      return true;
    }
    registry.registrarFor(key);
    return false;
  }
}

Flutter应用会默认自动生成GeneratedPluginRegistrant类,负责管理在pubspec文件中依赖插件注册于更新。 调用了PluginRegistry的registrarFor方法进行插件的注册。plugin可以提供与当前原生平台交互的能力。返回去看看FlutterActivity

FlutterActivity
代码不是很多,都贴出来看看吧

/**
 * Base class for activities that use Flutter.
 */
public class FlutterActivity extends Activity implements FlutterView.Provider, PluginRegistry, ViewFactory {
    private static final String TAG = "FlutterActivity";
    
    private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);

    // These aliases ensure that the methods we forward to the delegate adhere
    // to relevant interfaces versus just existing in FlutterActivityDelegate.
    private final FlutterActivityEvents eventDelegate = delegate;
    private final FlutterView.Provider viewProvider = delegate;
    private final PluginRegistry pluginRegistry = delegate;

    /**
     * Returns the Flutter view used by this activity; will be null before
     * {@link #onCreate(Bundle)} is called.
     */
    @Override
    public FlutterView getFlutterView() {
        return viewProvider.getFlutterView();
    }

    /**
     * Hook for subclasses to customize the creation of the
     * {@code FlutterView}.
     *
     * <p>The default implementation returns {@code null}, which will cause the
     * activity to use a newly instantiated full-screen view.</p>
     */
    @Override
    public FlutterView createFlutterView(Context context) {
        return null;
    }

    /**
     * Hook for subclasses to customize the creation of the
     * {@code FlutterNativeView}.
     *
     * <p>The default implementation returns {@code null}, which will cause the
     * activity to use a newly instantiated native view object.</p>
     */
    @Override
    public FlutterNativeView createFlutterNativeView() {
        return null;
    }

    @Override
    public boolean retainFlutterNativeView() {
        return false;
    }

    @Override
    public final boolean hasPlugin(String key) {
        return pluginRegistry.hasPlugin(key);
    }

    @Override
    public final <T> T valuePublishedByPlugin(String pluginKey) {
        return pluginRegistry.valuePublishedByPlugin(pluginKey);
    }

    @Override
    public final Registrar registrarFor(String pluginKey) {
        return pluginRegistry.registrarFor(pluginKey);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        eventDelegate.onCreate(savedInstanceState);
    }

    @Override
    protected void onStart() {
        super.onStart();
        eventDelegate.onStart();
    }

    @Override
    protected void onResume() {
        super.onResume();
        eventDelegate.onResume();
    }

    @Override
    protected void onDestroy() {
        eventDelegate.onDestroy();
        super.onDestroy();
    }

    @Override
    public void onBackPressed() {
        if (!eventDelegate.onBackPressed()) {
            super.onBackPressed();
        }
    }

    @Override
    protected void onStop() {
        eventDelegate.onStop();
        super.onStop();
    }

    @Override
    protected void onPause() {
        super.onPause();
        eventDelegate.onPause();
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        eventDelegate.onPostResume();
    }

    // @Override - added in API level 23
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        eventDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (!eventDelegate.onActivityResult(requestCode, resultCode, data)) {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        eventDelegate.onNewIntent(intent);
    }

    @Override
    public void onUserLeaveHint() {
        eventDelegate.onUserLeaveHint();
    }

    @Override
    public void onTrimMemory(int level) {
        eventDelegate.onTrimMemory(level);
    }

    @Override
    public void onLowMemory() {
        eventDelegate.onLowMemory();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        eventDelegate.onConfigurationChanged(newConfig);
    }
}

看代码并没有实现什么具体的功能, 与FlutterActivityDelegate之间就一个代理模式的实现。继承了Activity并实现了FlutterView.Provider、PluginRegistry、ViewFactory三个接口,内部有一个FlutterActivityDelegate对象,也是最终干活的苦力,其他的三个成员变量也都被设置为了delegate对象。这样实现我们也可以不用继承FlutterActivity写自己的Activity依样画葫芦的把FlutterActivityDelegate代理类挪过来就可以了,哈哈哈…下面我们看看FlutterActivity实现的这几个接口都是什么鬼:

FlutterView.Provider

 /**
     * Interface for those objects that maintain and expose a reference to a
     * {@code FlutterView} (such as a full-screen Flutter activity).
     *
     * <p>
     * This indirection is provided to support applications that use an activity
     * other than {@link io.flutter.app.FlutterActivity} (e.g. Android v4 support
     * library's {@code FragmentActivity}). It allows Flutter plugins to deal in
     * this interface and not require that the activity be a subclass of
     * {@code FlutterActivity}.
     * </p>
     */
    public interface Provider {
        /**
         * Returns a reference to the Flutter view maintained by this object. This may
         * be {@code null}.
         */
        FlutterView getFlutterView();
    }

FlutterView的内部定义的接口,返回一个FlutterView,FlutterView是负责展示Dart UI的容器。

PluginRegistry
注册用于原生平台交互的插件。

public interface PluginRegistry {
    Registrar registrarFor(String pluginKey);
    boolean hasPlugin(String pluginKey);
    <T> T valuePublishedByPlugin(String pluginKey);

    interface Registrar {
        Activity activity();
        Context context();
        Context activeContext();
        BinaryMessenger messenger();
        TextureRegistry textures();
        PlatformViewRegistry platformViewRegistry();
        FlutterView view();
        String lookupKeyForAsset(String asset);
        String lookupKeyForAsset(String asset, String packageName);
        Registrar publish(Object value);
        Registrar addRequestPermissionsResultListener(RequestPermissionsResultListener listener);
        Registrar addActivityResultListener(ActivityResultListener listener);
        Registrar addNewIntentListener(NewIntentListener listener);
        Registrar addUserLeaveHintListener(UserLeaveHintListener listener);
        Registrar addViewDestroyListener(ViewDestroyListener listener);
    }
    interface RequestPermissionsResultListener {
        boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
    }
    interface ActivityResultListener {
        boolean onActivityResult(int requestCode, int resultCode, Intent data);
    }
    interface NewIntentListener {
        boolean onNewIntent(Intent intent);
    }
    interface UserLeaveHintListener {
        void onUserLeaveHint();
    }
    interface ViewDestroyListener {
        boolean onViewDestroy(FlutterNativeView view);
    }
    interface PluginRegistrantCallback {
        void registerWith(PluginRegistry registry);
    }
}

内部有7个接口,现在只关注FlutterActivity所要实现的三个方法:
registrarFor:注册插件并返回这个插件的Registrar;
hasPlugin:指定的插件名称是否已经存在;
valuePublishedByPlugin:从已注册的插件集合中获取指定的插件。

ViewFactory

    /**
     * Specifies the mechanism by which Flutter views are created during the
     * operation of a {@code FlutterActivityDelegate}.
     *
     * <p>A delegate's view factory will be consulted during
     * {@link #onCreate(Bundle)}. If it returns {@code null}, then the delegate
     * will fall back to instantiating a new full-screen {@code FlutterView}.</p>
     *
     * <p>A delegate's native view factory will be consulted during
     * {@link #onCreate(Bundle)}. If it returns {@code null}, then the delegate
     * will fall back to instantiating a new {@code FlutterNativeView}. This is
     * useful for applications to override to reuse the FlutterNativeView held
     * e.g. by a pre-existing background service.</p>
     */
    public interface ViewFactory {
        FlutterView createFlutterView(Context context);
        FlutterNativeView createFlutterNativeView();

        /**
         * Hook for subclasses to indicate that the {@code FlutterNativeView}
         * returned by {@link #createFlutterNativeView()} should not be destroyed
         * when this activity is destroyed.
         */
        boolean retainFlutterNativeView();
    }

FlutterActivityDelegate类中定义的接口,用来创建FlutterView和FlutterNativeView的,最后一个方法是用于控制是否在页面销毁时同步销毁FlutterNativeView。这三个方法在FlutterActivity的实现中均是返回的默认值。

    private final FlutterActivityEvents eventDelegate = delegate;
    private final FlutterView.Provider viewProvider = delegate;
    private final PluginRegistry pluginRegistry = delegate;

三个成员变量实际上都指向了delegate,最终FlutterActivity中所有的调用都会调用到FlutterActivityDelegate中。

FlutterActivityDelegate
呀!现在找到真正干事情的老实人了,我们来简单看一下他的属性和构造方法:

public final class FlutterActivityDelegate
        implements FlutterActivityEvents,
                   FlutterView.Provider,
                   PluginRegistry {
   	private final Activity activity;
    private final ViewFactory viewFactory;
    private FlutterView flutterView;
    private View launchView;
    
 	public FlutterActivityDelegate(Activity activity, ViewFactory viewFactory) {
        this.activity = Preconditions.checkNotNull(activity);
        this.viewFactory = Preconditions.checkNotNull(viewFactory);
    }
}       

viewFactory:这个也就是FlutterActivity,创建FlutterActivityDelegate时传入的。
flutterView:当前Activity中所要显示的View。
launchView:加载Flutter时等待界面,这个View会根据在AndroidManifest中的配置来控制是否展示。
与FlutterActivity不同的是它实现了FlutterActivityEvents:

public interface FlutterActivityEvents
        extends ComponentCallbacks2,
                ActivityResultListener,
                RequestPermissionsResultListener {
    /**
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    void onCreate(Bundle savedInstanceState);

    /**
     * @see android.app.Activity#onNewIntent(Intent)
     */
    void onNewIntent(Intent intent);

    /**
     * @see android.app.Activity#onPause()
     */
    void onPause();

    /**
     * @see android.app.Activity#onStart()
     */
    void onStart();

    /**
     * @see android.app.Activity#onResume()
     */
    void onResume();

    /**
     * @see android.app.Activity#onPostResume()
     */
    void onPostResume();

    /**
     * @see android.app.Activity#onDestroy()
     */
    void onDestroy();

    /**
     * @see android.app.Activity#onStop()
     */
    void onStop();

    /**
     * Invoked when the activity has detected the user's press of the back key.
     *
     * @return {@code true} if the listener handled the event; {@code false}
     *     to let the activity continue with its default back button handling.
     * @see android.app.Activity#onBackPressed()
     */
    boolean onBackPressed();

    /**
     * @see android.app.Activity#onUserLeaveHint()
     */
    void onUserLeaveHint();
}

FlutterActivityEvents定义了与Activity生命周期和一些特定交互相关的方法,便于实现代理模式。

回到FlutterActivityDelegate中,在FlutterActivity创建是首先会走onCreate方法,因FlutterActivityDelegate是FlutterActivity的代理实现,我们定位到这里

   @Override
    public void onCreate(Bundle savedInstanceState) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(0x40000000);
            window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
        }

        String[] args = getArgsFromIntent(activity.getIntent());
        FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);

        flutterView = viewFactory.createFlutterView(activity);
        if (flutterView == null) {
            FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
            flutterView = new FlutterView(activity, null, nativeView);
            flutterView.setLayoutParams(matchParent);
            activity.setContentView(flutterView);
            launchView = createLaunchView();
            if (launchView != null) {
                addLaunchView();
            }
        }

        if (loadIntent(activity.getIntent())) {
            return;
        }

        String appBundlePath = FlutterMain.findAppBundlePath();
        if (appBundlePath != null) {
            runBundle(appBundlePath);
        }
    }

onCreate方法做了以下事情:
1、根据sdk版本设置状态条的样式;
2、确认Flutter engine初始化,没有,初始化Flutter engine;
3、创建FlutterView;
4、添加FlutterView到页面中(展示Dart UI);
5、创建launchView;
6、launchView不为null,添加到页面中,在FlutterView渲染完成第一帧时移除;
7、加载Dart页面

step 2 初始化Flutter Engine:FlutterMain ensureInitializationComplete方法最终调用了FlutterLoader的ensureInitializationComplete方法

public void ensureInitializationComplete(@NonNull Context applicationContext, @Nullable String[] args) {
        if (initialized) {
            return;
        }
        if (Looper.myLooper() != Looper.getMainLooper()) {
          throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
        }
        if (settings == null) {
          throw new IllegalStateException("ensureInitializationComplete must be called after startInitialization");
        }
        try {
            if (resourceExtractor != null) {
                resourceExtractor.waitForCompletion();
            }

            List<String> shellArgs = new ArrayList<>();
            shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");

            ApplicationInfo applicationInfo = getApplicationInfo(applicationContext);
            shellArgs.add("--icu-native-lib-path=" + applicationInfo.nativeLibraryDir + File.separator + DEFAULT_LIBRARY);

            if (args != null) {
                Collections.addAll(shellArgs, args);
            }

            String kernelPath = null;
            if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) {
                String snapshotAssetPath = PathUtils.getDataDirectory(applicationContext) + File.separator + flutterAssetsDir;
                kernelPath = snapshotAssetPath + File.separator + DEFAULT_KERNEL_BLOB;
                shellArgs.add("--" + SNAPSHOT_ASSET_PATH_KEY + "=" + snapshotAssetPath);
                shellArgs.add("--" + VM_SNAPSHOT_DATA_KEY + "=" + vmSnapshotData);
                shellArgs.add("--" + ISOLATE_SNAPSHOT_DATA_KEY + "=" + isolateSnapshotData);
            } else {
                shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME + "=" + aotSharedLibraryName);

                // Most devices can load the AOT shared library based on the library name
                // with no directory path.  Provide a fully qualified path to the library
                // as a workaround for devices where that fails.
                shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME + "=" + applicationInfo.nativeLibraryDir + File.separator + aotSharedLibraryName);
            }

            shellArgs.add("--cache-dir-path=" + PathUtils.getCacheDirectory(applicationContext));
            if (settings.getLogTag() != null) {
                shellArgs.add("--log-tag=" + settings.getLogTag());
            }

            String appStoragePath = PathUtils.getFilesDir(applicationContext);
            String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);
            FlutterJNI.nativeInit(applicationContext, shellArgs.toArray(new String[0]),
                kernelPath, appStoragePath, engineCachesPath);

            initialized = true;
        } catch (Exception e) {
            Log.e(TAG, "Flutter initialization failed.", e);
            throw new RuntimeException(e);
        }
    }

确保Flutter已经初始化完成,前面介绍打开应用时在Debug模式下Flutter会异步进行资源解压缩操作,resourceExtractor.waitForCompletion()保证前面的异步任务执行完毕才能往下执行。当任务完成后,配置一些参数调用FlutterJNI.nativeInit(applicationContext, shellArgs.toArray(new String[0]),
kernelPath, appStoragePath, engineCachesPath)Native方法来完成C++层的Flutter Engine的初始化。上面代码看到在DEBUG、JIT_RELEASE和RELEASE配置参数不同,我们先只关注RELEASE模式,配置了AOT编译模式下需要的参数:
1、也就是flutter应用编译生成的so库名称(这个是可在Manifest中配置的,默认名称为libapp.so);
2、还有so库所在的绝对路径,便于底层engine加载。
再就是向底层engine传入了三个应用路径:
kernelPath :在RELEASE下这个路径 为null;
appStoragePath:/data/user/0/package/files
engineCachesPath:/data/user/0/package/code_cache

step 3、4 创建FlutterView

   flutterView = viewFactory.createFlutterView(activity);
        if (flutterView == null) {
            FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
            flutterView = new FlutterView(activity, null, nativeView);
            flutterView.setLayoutParams(matchParent);
            activity.setContentView(flutterView);
            launchView = createLaunchView();
            if (launchView != null) {
                addLaunchView();
            }
        }

FlutterView为Dart UI的跟布局,会优先从ViewFactory的createFlutterView方法中获取,这里的viewFactory就是FlutterActivity,方法没有实现默认返回null,所以一定会进入下面的判断,在下面代码中也是会调用viewFactory.createFlutterNativeView优先获取FlutterNativeView,同样也是返回了null,接着new了个FlutterView并设置为全屏显示添加到Activity中。
看看FlutterView的创建:卧槽进去了一下比较复杂些,先略过后面在单个分析,我们现在只要知道它继承自SurfaceView并实现了BinaryMessenger接口(原生平台与Flutter通讯的消息)和TextureRegistry接口(待了解)。好吧我们回来接着往下看。

step 5 创建launchView
默认会创建一个launchView用于在加载Flutter页面时展示的等待画面,会获取当前Activity配置的WindowBackground Drawable资源,为null则不创建。

 private View createLaunchView() {
        if (!showSplashScreenUntilFirstFrame()) {
            return null;
        }
        final Drawable launchScreenDrawable = getLaunchScreenDrawableFromActivityTheme();
        if (launchScreenDrawable == null) {
            return null;
        }
        final View view = new View(activity);
        view.setLayoutParams(matchParent);
        view.setBackground(launchScreenDrawable);
        return view;
    }
    private Drawable getLaunchScreenDrawableFromActivityTheme() {
        TypedValue typedValue = new TypedValue();
        if (!activity.getTheme().resolveAttribute(
            android.R.attr.windowBackground,
            typedValue,
            true)) {
            return null;
        }
        if (typedValue.resourceId == 0) {
            return null;
        }
        try {
            return activity.getResources().getDrawable(typedValue.resourceId);
        } catch (NotFoundException e) {
            Log.e(TAG, "Referenced launch screen windowBackground resource does not exist");
            return null;
        }
    }

step 6 添加launchView
在以上执行创建launchView成功后会将launchView添加至Activity中,同样也是设置为全屏,这时launchView会覆盖在FlutterView的上层,这样在加载Flutter时看到的是launchView。

 private void addLaunchView() {
        if (launchView == null) {
            return;
        }

        activity.addContentView(launchView, matchParent);
        flutterView.addFirstFrameListener(new FlutterView.FirstFrameListener() {
            @Override
            public void onFirstFrame() {
                FlutterActivityDelegate.this.launchView.animate()
                    .alpha(0f)
                    // Use Android's default animation duration.
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            // Views added to an Activity's addContentView is always added to its
                            // root FrameLayout.
                            ((ViewGroup) FlutterActivityDelegate.this.launchView.getParent())
                                .removeView(FlutterActivityDelegate.this.launchView);
                            FlutterActivityDelegate.this.launchView = null;
                        }
                    });

                FlutterActivityDelegate.this.flutterView.removeFirstFrameListener(this);
            }
        });

        // Resets the activity theme from the one containing the launch screen in the window
        // background to a blank one since the launch screen is now in a view in front of the
        // FlutterView.
        //
        // We can make this configurable if users want it.
        activity.setTheme(android.R.style.Theme_Black_NoTitleBar);
    }

添加launchView时同时注册了Flutter渲染第一帧的回调,在收到回调后移除launchView让FlutterView得以显示。

step 7 加载Dart页面

  private boolean loadIntent(Intent intent) {
        String action = intent.getAction();
        if (Intent.ACTION_RUN.equals(action)) {
            String route = intent.getStringExtra("route");
            String appBundlePath = intent.getDataString();
            if (appBundlePath == null) {
                // Fall back to the installation path if no bundle path was specified.
                appBundlePath = FlutterMain.findAppBundlePath();
            }
            if (route != null) {
                flutterView.setInitialRoute(route);
            }

            runBundle(appBundlePath);
            return true;
        }

        return false;
    }

这里可以指定Flutter Bundle的文件路径和route参数,这个route参数最终在加载Dart UI入口中带入window.defaultRouteName获取,
例:void main() => runApp(_widgetForRoute(window.defaultRouteName));
Flutter Bundle文件路径现在有点模糊后续再看(因在上边的分析中我们知道在DEBUG模式中是存在资源文件解压到应用文件中的可以理解通,但在RELEASE中没有资源解压缩的操作,只有个libapp.so文件,而这个路径也在启动Flutter engine中传入了,所以就有点模糊了,以后再看…)。
以上可知两个参数的传入只有在Intent action等于Intent.ACTION_RUN的情况下可用,否则不执行任何操作,直接返回false。但最终都会执行到runBundle(String appBundlePath)方法

  private void runBundle(String appBundlePath) {
        if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
            FlutterRunArguments args = new FlutterRunArguments();
            args.bundlePath = appBundlePath;
            args.entrypoint = "main";
            flutterView.runFromBundle(args);
        }
    }

首先会判断当前的FlutterView应用是否已经在运行,如果已经运行就不会重新执行。没有运行会构建运行参数,entrypoint为dart应用的入口函数名,调用FlutterView的runFromBundle来运行。

再看看FlutterActivityDelegate的其他方法,都是Activity的生命周期方法调用,最终都通知到了FlutterView。

通过这篇分析了解到FlutterActivity只是一个空壳,我们可以自定义自己的实现,真正干活的是FlutterActivityDelegate。加载运行flutter应用的是FlutterView,而FlutterView通过FlutterActivityDelegate响应了Activity的生命周期。最终的核心在FlutterView。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Flutter应用模板是为开发Flutter应用程序提供的基本框架和结构。它为开发者提供了一种快速启动新项目的方法,减少了从零开始建立应用程序所需的时间和努力。 Flutter应用模板通常包括以下内容: 1. 主界面布局:应用模板通常包含一个主界面布局,作为用户与应用程序进行交互的主要界面。它可以包含各种组件,如按钮、文本框和图像等,用于构建用户界面。 2. 导航栏和底部导航:应用模板通常包含导航栏和/或底部导航栏,用于在应用程序的不同页面之间进行导航。导航栏通常包含应用程序的标题和一些常见的导航按钮,如返回按钮。 3. 状态管理:Flutter应用模板通常使用一些状态管理解决方案,如Provider或Bloc,来帮助开发者跟踪和管理应用程序的状态。这些解决方案可以帮助应用程序在不同的页面和组件之间共享数据。 4. 路由管理:应用模板通常包含路由管理,用于定义应用程序中不同页面之间的路由。这使得开发者可以方便地导航到不同的页面,并管理页面之间的传递参数。 5. 主题和样式:应用模板通常包含主题和样式定义,用于控制应用程序的外观和感觉。开发者可以根据需要修改这些样式,以适应其特定的设计需求。 总的来说,Flutter应用模板提供了一个开箱即用的基础架构,可以帮助开发者快速启动新项目,并提供了一些常见的功能和组件,以加速开发流程。开发者可以在这些模板的基础上进行修改和扩展,以满足其特定的需求。 ### 回答2: Flutter应用模板是一种用于快速搭建Flutter应用的基础代码结构和组织方式。它提供了一套默认的文件夹结构和相关文件,使开发人员能够更高效地开发Flutter应用。 一个典型的Flutter应用模板包括以下几个主要文件和文件夹: 1. `lib`文件夹:这是应用的主要代码目录,包含了应用的各种逻辑和界面组件。 2. `main.dart`文件:这是应用的入口文件,其中包括了应用的启动配置和主要逻辑。 3. `pubspec.yaml`文件:这是Flutter应用的配置文件,用于声明应用所依赖的包和资源文件。 4. `assets`文件夹:这是应用的资源文件夹,包括图片、字体等静态资源文件。 5. `android`和`ios`文件夹:这两个文件夹分别是Android和iOS平台的配置文件和代码,用于构建和运行应用Flutter应用模板还可以包含其他一些常用的功能和模块,比如路由管理、国际化、网络请求等。这些功能通常通过引入相应的Flutter包或代码实现,以提高开发效率和代码质量。 使用Flutter应用模板,开发人员可以快速搭建应用的基本架构,减少重复劳动,提高开发效率。同时,模板还提供了良好的代码组织和结构,使得代码更易于维护和扩展。开发人员可以根据具体项目需求,对模板进行个性化定制,以满足应用的特定需求。 总之,Flutter应用模板是一种帮助开发人员快速搭建Flutter应用的基础代码结构和组织方式。它提供了一系列默认的文件和文件夹,以及常用功能和模块的集成,从而提高了开发效率和代码质量。 ### 回答3: Flutter应用模板是一种预定义的项目结构和代码框架,旨在提供一个起点,帮助开发人员更快速地构建Flutter应用程序。 Flutter应用模板通常包含以下主要部分: 1. 主要目录结构:应用模板通常会按照一定的规范来组织代码文件和资源文件,例如将源代码放在“lib”目录下,将资源文件放在“assets”目录下,将测试文件放在“test”目录下等。这些规范可以让项目更加有条理和易于维护。 2. 配置文件:应用模板会提供一些默认的配置文件,例如“pubspec.yaml”文件用于管理项目的依赖库和资源文件,以及“AndroidManifest.xml”和“Info.plist”文件用于配置Android和iOS应用的一些基本信息。 3. 路由管理:应用模板通常会提供一种路由管理机制,用于管理应用中不同页面的跳转和传递参数,帮助开发人员更好地组织和管理页面的切换流程。 4. 预定义UI组件:应用模板往往会包含一些常用的UI组件,例如按钮、文本输入框、图片容器等,开发人员可以直接在这些组件的基础上进行定制和扩展,提高开发效率。 5. 状态管理:应用模板通常会提供一种状态管理机制,例如使用“Provider”或“Bloc”等库来管理全局状态和组件内部的状态,以便实现页面间的数据共享和状态更新。 使用Flutter应用模板可以节省开发人员的精力和时间,快速搭建项目的骨架,并且遵循一定的结构和规范,有助于团队协作和项目的扩展与维护。当然,开发人员也可以根据实际需求进行定制和修改,以满足特定的业务需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值