You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:WebView中OnPageStarted/OnPageFinished重复触发问题求助

问题:WebView加载动画反复触发的解决办法

在Flutter项目中使用Cubit状态管理,通过WebView的onPageStarted和onPageFinished控制页面加载动画,但这两个方法在应用生命周期内持续重复触发,导致加载动画反复显示,需实现仅在页面首次加载时触发这两个方法。


解决思路

核心是通过标记首次加载状态,避免后续页面跳转或重加载时重复触发动画:

  • 在Cubit中新增状态变量,记录页面是否完成首次加载
  • 在onPageStarted和onPageFinished中添加判断,仅当首次加载时更新加载状态
  • 将WebViewController的创建移至StatefulWidget的初始化阶段,避免每次build时重建控制器导致重复绑定回调

步骤1:修改AppCubit,添加首次加载标记

在AppCubit中新增状态变量,控制仅首次加载时更新动画状态:

class AppCubit extends Cubit<AppState> {
  AppCubit() : super(AppInitialState());

  bool isLoading = false;
  // 新增:标记首次加载是否完成
  bool isFirstLoadCompleted = false;

  void completeLoading({required bool trueFalse}) {
    // 仅首次加载时更新isLoading状态
    if (!isFirstLoadCompleted) {
      isLoading = trueFalse;
      emit(AppLoadingState());
    }
    // 加载完成后标记首次加载结束
    if (!trueFalse) {
      isFirstLoadCompleted = true;
    }
  }
}

步骤2:修改WebViewPage为StatefulWidget并优化控制器创建

原代码在StatelessWidget的build方法中每次重建WebViewController,是重复触发回调的关键原因。改为StatefulWidget后,在initState中仅初始化一次控制器:

修改后的完整代码

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_offline/flutter_offline.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../Module/InternetConnection/internetconnection.dart';
import '../Shared/Component.dart';
import '../Shared/constant.dart';
import '../Shared/cubit/app_cubit.dart';

class WebViewPage extends StatefulWidget {
  const WebViewPage({Key? key, required this.urlPage, required this.titlePage}) : super(key: key);
  final String urlPage;
  final String titlePage;

  @override
  State<WebViewPage> createState() => _WebViewPageState();
}

class _WebViewPageState extends State<WebViewPage> {
  // 仅初始化一次WebViewController
  late final WebViewController _webViewController;

  @override
  void initState() {
    super.initState();
    _webViewController = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(
        NavigationDelegate(
          onPageStarted: (String url) {
            debugPrint('\n onPageStarted : Start Loading\n');
            // 仅首次加载时触发加载动画
            if (!AppCubit.get(context).isFirstLoadCompleted) {
              AppCubit.get(context).completeLoading(trueFalse: true);
            }
          },
          onPageFinished: (String url) {
            debugPrint('\n onPageFinished : Finish Loading\n');
            // 仅首次加载时关闭加载动画
            if (!AppCubit.get(context).isFirstLoadCompleted) {
              AppCubit.get(context).completeLoading(trueFalse: false);
            }
          },
        ),
      )
      ..loadRequest(Uri.parse(widget.urlPage));
  }

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => AppCubit(),
      child: BlocBuilder<AppCubit, AppState>(builder: (context, state) {
        return OfflineBuilder(
          connectivityBuilder: (BuildContext context, ConnectivityResult connectivity, Widget child) {
            final bool connectedWithNetwork = connectivity != ConnectivityResult.none;
            if (connectedWithNetwork) {
              return Scaffold(
                appBar: buildAppBar(context, title: widget.titlePage),
                body: SizedBox(
                  width: double.infinity,
                  height: double.infinity,
                  child: Stack(
                    children: [
                      WebViewWidget(controller: _webViewController),
                      AppCubit.get(context).isLoading
                          ? Center(
                              child: Container(
                                width: double.infinity,
                                height: double.infinity,
                                color: Colors.white70.withOpacity(0.1),
                                child: SpinKitFadingCircle(
                                  color: blue,
                                  size: 100.0,
                                ),
                              ),
                            )
                          : const SizedBox.shrink(),
                    ],
                  ),
                ),
              );
            } else {
              return const NotConnection();
            }
          },
          child: const SizedBox.shrink(),
        );
      }),
    );
  }
}

关键优化点说明

  • 将WebViewPage改为StatefulWidget,确保WebViewController仅初始化一次,避免每次build时重新绑定回调和加载页面
  • 通过Cubit的isFirstLoadCompleted变量限制动画仅在首次加载时触发
  • 用SizedBox.shrink()替代原代码中的const Stack(),减少不必要的Widget渲染

内容的提问来源于stack exchange,提问作者Asma'a Alndhari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:44:56