본문 바로가기

개발하자

WebView Flurter에서 로딩 표시기를 표시하는 방법?

반응형

WebView Flurter에서 로딩 표시기를 표시하는 방법?

화면에 표시되는 웹 보기 데이터 전에 Loading을 먼저 표시하고 싶습니다. 어떻게 그럴 수 있죠?

내 코드는 다음과 같다:

class WebDetailPage extends StatelessWidget {
  final String title;
  final String webUrl;

  final Completer<WebViewController> _controller =
      Completer<WebViewController>();

  WebDetailPage({
    @required this.title,
    @required this.webUrl,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colour.white,
        title: Text(title, style: TextStyle(color: Colour.midnightBlue)),
        leading: IconButton(
            icon: Icon(Icons.arrow_back, color: Colour.midnightBlue),
            onPressed: () => Navigator.of(context).pop()),
      ),
      body: Center(
        child: WebView(
          initialUrl: webUrl,
          javascriptMode: JavascriptMode.unrestricted,
          onWebViewCreated: (WebViewController webViewController) {
            _controller.complete(webViewController);
          },
        ),
      )
    );
  }
}

누가 이 문제를 도와줄 수 있나요? 왜냐하면 나는 이미 그것에 대해 검색하고 조사했지만 여전히 해결책을 찾을 수 있기 때문이다.




전체 예제

class WebViewState extends State<WebViewScreen>{

  String title,url;
  bool isLoading=true;
  final _key = UniqueKey();
  
  WebViewState(String title,String url){
    this.title=title;
    this.url=url;
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
          title: Text(this.title,style: TextStyle(fontWeight: FontWeight.w700)),centerTitle: true
      ),
      body: Stack(
        children: <Widget>[
          WebView(
            key: _key,
            initialUrl: this.url,
            javascriptMode: JavascriptMode.unrestricted,
            onPageFinished: (finish) {
              setState(() {
                isLoading = false;
              });
            },
          ),
          isLoading ? Center( child: CircularProgressIndicator(),)
                    : Stack(),
        ],
      ),
    );
  }

}

나는 웹뷰 세트 로딩 표시기 위에 위젯을 사용할 뿐이다. 웹뷰 호출 시 변수 값을 설정하고 투명 컨테이너를 설정합니다.




할 수 있어요. 네, 맞습니다.

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() => runApp(MaterialApp(home: MyApp()));

class MyApp extends StatelessWidget {

  static Future<String> get _url async {
    await Future.delayed(Duration(seconds: 1));
    return 'https://flutter.dev/';
  }

  @override
  Widget build(BuildContext context) => Scaffold(
    body: Center(
      child:FutureBuilder(
        future: _url,
        builder: (BuildContext context, AsyncSnapshot snapshot) => snapshot.hasData
        ? WebViewWidget(url: snapshot.data,)
        : CircularProgressIndicator()),
  ),);
}

class WebViewWidget extends StatefulWidget {
  final String url;
  WebViewWidget({this.url});

  @override
  _WebViewWidget createState() => _WebViewWidget();
}

class _WebViewWidget extends State<WebViewWidget> {
  WebView _webView;
  @override
  void initState() {
    super.initState();
     _webView = WebView(
      initialUrl: widget.url,
      javascriptMode: JavascriptMode.unrestricted,
    );
  }

  @override
  void dispose() {
    super.dispose();
    _webView = null;
  }

  @override
  Widget build(BuildContext context) => _webView;
}



전체 예제

class WebViewState extends State<WebViewScreen>{

  String title,url;
  bool isLoading=true;
  final _key = UniqueKey();

  WebViewState(String title,String url){
    this.title=title;
    this.url=url;
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
          title: Text(this.title,style: TextStyle(fontWeight: FontWeight.w700)),centerTitle: true
      ),
      body: Stack(
        children: <Widget>[
          WebView(
            key: _key,
            initialUrl: this.url,
            javascriptMode: JavascriptMode.unrestricted,
            onPageFinished: (finish) {
              setState(() {
                isLoading = false;
              });
            },
          ),
          isLoading ? Center( child: CircularProgressIndicator(),)
                    : Stack(),
        ],
      ),
    );
  }

}



인덱스 스택 위젯을 사용하면 인덱스에 따라 위젯을 전환할 수 있습니다. 또한 웹 뷰의 onPageStarted 및 onPageFinished 특성을 사용합니다. 상태 관리를 사용하여 페이지가 로드되기 시작할 때와 페이지 로드가 완료될 때 색인 값을 변경합니다.

num pos = 1;

인빌드 메서드

return Scaffold(
        body: IndexedStack(index: pos, children: <Widget>[
      WebView(
        initialUrl: 'http://pub.dev/',
        javascriptMode: JavascriptMode.unrestricted,
        onPageStarted: (value) {
          setState(() {
            pos = 1;
          });
        },
        onPageFinished: (value) {
          setState(() {
            pos = 0;
          });
        },
      ),
      Container(
        child: Center(child: CircularProgressIndicator()),
      ),
    ]));



BLOCK, 스트림 및 상태 비저장 위젯을 사용할 수 있습니다


import 'dart:async';

import 'package:rxdart/subjects.dart';

class LoadingWebPageBloc  {
//Controllers
  final BehaviorSubject<bool> _loadingWebPageController = BehaviorSubject<bool>.seeded(true);

  //Sinks
  Function(bool) get changeLoadingWebPage => _loadingWebPageController.sink.add;

  //Streams
  Stream<bool> get loadingWebPageStream => _loadingWebPageController.stream.asBroadcastStream();

  @override
  void dispose() {
    _loadingWebPageController.close();
    super.dispose();
  }
}

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class CustomWebPagePreview extends StatelessWidget {
  final String url;
  CustomWebPagePreview({@required this.url});

  final LoadingWebPageBloc loadingWebPageBloc = LoadingWebPageBloc();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: appBar,
        body: Container(
          child: Stack(
            children: <Widget>[
              WebView(
                initialUrl: url,
                javascriptMode: JavascriptMode.unrestricted,
                onPageStarted: (value) {
                  loadingWebPageBloc.changeloading(true);
                },
                onPageFinished: (value) {
                  loadingWebPageBloc.changeloading(false);
                },
              ),
              StreamBuilder<bool>(
                stream: loadingWebPageBloc.loading,
                initialData: true,
                builder: (context, snap) {
                  if (snap.hasData && snap.data == true) {
                    return Center(
                      child: CircularProgressIndicator(),
                    );
                  }
                  return SizedBox();
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}



스택 및 가시성 위젯만 사용

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class MyWebView extends StatefulWidget {
final String url;
const MyWebView({Key? key, this.url = ''}) : super(key: key);

@override
State<MyWebView> createState() => _MyWebViewState();
}

class _MyWebViewState extends State<MyWebView> {
  bool isLoading = true;
  @override
  void initState() {
  super.initState();
  if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  if (Platform.isIOS) WebView.platform = CupertinoWebView();
}

@override
Widget build(BuildContext context) {
return SafeArea(
  child: Scaffold(
    appBar: AppBar(
      backgroundColor: Colors.transparent,
      elevation: 0,
    ),
    body: Stack(
      children: [
        WebView(
          initialUrl: widget.url,
          onPageFinished: (finish) {
            setState(() {
              isLoading = false;
            });
          },
          javascriptMode: JavascriptMode.unrestricted,
        ),
        Visibility(
          visible: isLoading,
          child: const Center(
            child: CircularProgressIndicator(),
          ),
        )
      ],
    ),
    bottomNavigationBar: BottomAppBar(
      child: Row(),
      ),
    ),
  );
 }
}



전체 예제

스택 및 가시성 위젯 사용

  bool showLoader = true;
    Stack(
       children: [
         Container(
           height: _updatedHeight,
           child: GestureDetector(
             onHorizontalDragUpdate: (updateDetails) {},
             child: Container(
               margin: EdgeInsets.only(right: 16,left: 16),
               width: MediaQuery.of(context).size.width,
               child: WebView(
                 initialUrl: "url",
                javascriptMode: JavascriptMode.unrestricted,
                 onWebViewCreated: (WebViewController webViewController) {
                   _completerController.complete(webViewController);
                   mainWebController = webViewController;
                 },
                 onProgress: (int progress) {
                   if(progress == 100){
                     setState(() {
                       showLoader = false;
                     });
                   }
                 },
                 onPageStarted: (String url) {
                //   print("WebView :: onPageStarted :: $url");
                 },
                 onPageFinished: (url) async {
                   double documentElementHeight = double.parse(
                       await mainWebController.runJavascriptReturningResult("document.documentElement.scrollHeight;"));
                      // print("WebView :: onPageFinished :: documentElementHeight = $documentElementHeight");
                       setState(() {
                         _updatedHeight = documentElementHeight; 
                       });
                      });
                    },
                 navigationDelegate: getNavigationDelegate,
               ),
             ),
           ),
         ),
        Visibility(
         visible: showLoader,
          child:Padding(
            padding: const EdgeInsets.only(top: 50),
            child: Container(
             width: MediaQuery.of(context).size.width,
             child: Center(
               child: AWProgressIndicatorWidget(),
             ),
         ),
          )
        )
       ],
     )



Google Codelab

내 생각에 이 해결책은 이다.

자세한 내용은 의 공식 예를 참조하십시오.

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class WebViewStack extends StatefulWidget {
  const WebViewStack({super.key});

  @override
  State<WebViewStack> createState() => _WebViewStackState();
}

class _WebViewStackState extends State<WebViewStack> {
  var loadingPercentage = 0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebView(
          initialUrl: 'https://flutter.dev',
          onPageStarted: (url) {
            setState(() {
              loadingPercentage = 0;
            });
          },
          onProgress: (progress) {
            setState(() {
              loadingPercentage = progress;
            });
          },
          onPageFinished: (url) {
            setState(() {
              loadingPercentage = 100;
            });
          },
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }
}



클린 코드를 원하는 개발자를 위해, 이것을 시도해보세요:

위에

import 'package:flutter/material.dart';
import 'package:ProjectName/src/app.dart'; //replace with your project name
 
void main() => runApp(MyApp());

위에

import 'package:flutter/material.dart';
import 'package:ProjectName/src/webview_container.dart'; //replace with your project name

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
    home: Scaffold(
        body: WebViewClass('https://www.testurl.com', 'test') //replace with your url
       )
      );
  }
}

위에

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class WebViewClass extends StatefulWidget {
  final url;
  final title;
  WebViewClass(this.url, this.title);


  @override
  createState() => WebViewState(this.url, this.title);
}
class WebViewState extends State<WebViewClass>{

  var _url;
  var _title;

  int position = 1 ;

  final key = UniqueKey();
  WebViewState(this._url, this._title);

  doneLoading(String A) {
    setState(() {
      position = 0;
    });
  }

  startLoading(String A){
    setState(() {
      position = 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
    child: Scaffold(
      body: IndexedStack(
      index: position,
      children: <Widget>[

      WebView(
        zoomEnabled: false, //I have disabled zoom functionality on the app
        initialUrl: _url,
        javascriptMode: JavascriptMode.unrestricted,
        key: key ,
        onPageFinished: doneLoading,
        onPageStarted: startLoading,
        ),

       Container(
        color: Colors.white,
        child: Center(
          child: CircularProgressIndicator()),
        ),
        
      ])
  ),
    );
  }
}



onPageStarted가 더 좋습니다! URL이 로드되기 시작한 후 로드 표시기가 중지되었기 때문입니다.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBarCommon(
        title: Text(
          widget.title,
        ),
      ),
      body: Stack(
        children: <Widget>[
          WebView(
            initialUrl: widget.url,
            javascriptMode: JavascriptMode.unrestricted,
            onPageStarted: (started){
              setState(() {
                isLoading = false;
              });
            },
          ),
          isLoading
              ? const Center(
                  child: LoadingIndicator(),
                )
              : Stack(),
        ],
      ),
    );
  }



만약 누군가 webview_flutter 버전 4.0.2를 사용하여 이것을 우연히 발견한다면, 우리는 's를 사용하고 onPageFinished 콜백을 설정해야 한다.

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class PrivacyPolicyPage extends StatefulWidget {
  static const routeName = "/privacy_policy";

  const PrivacyPolicyPage({super.key});

  @override
  State<PrivacyPolicyPage> createState() => _PrivacyPolicyPageState();
}

class _PrivacyPolicyPageState extends State<PrivacyPolicyPage> {
  late final WebViewController _controller;
  bool _loading = true;

  @override
  void initState() {
    _controller = WebViewController.fromPlatformCreationParams(
        const PlatformWebViewControllerCreationParams())
      ..setNavigationDelegate(NavigationDelegate(
          onPageFinished: (_) => setState(() {
                _loading = false;
              })))
      ..setJavaScriptMode(JavaScriptMode.disabled)
      ..loadFlutterAsset("assets/privacy_policy.html");
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Privacy Policy')),
      body: Stack(children: [
        WebViewWidget(controller: _controller),
        if (_loading) const Center(child: CircularProgressIndicator())
      ]),
    );
  }
}


반응형