आपके Flutter ऐप्लिकेशन में वेबव्यू जोड़ना

1. परिचय

पिछली बार अपडेट किया गया: 19-10-2021

वेबव्यू Flutter प्लगिन की मदद से अपने Android या iOS Flutter ऐप्लिकेशन में वेबव्यू विजेट जोड़ा जा सकता है. iOS पर वेबव्यू विजेट, WKWebView के साथ काम करता है. वहीं, Android पर वेबव्यू विजेट, WebView के साथ काम करता है. प्लग इन, वेब व्यू पर Flutter विजेट रेंडर कर सकता है. उदाहरण के लिए, वेब व्यू के ऊपर ड्रॉप-डाउन मेन्यू रेंडर करना मुमकिन है.

आपको क्या बनाना होगा

इस कोडलैब में, सिलसिलेवार तरीके से मोबाइल ऐप्लिकेशन बनाया जा सकता है, जिसमें Flutter SDK टूल का इस्तेमाल करके वेबव्यू दिखाया जाता है. आपका ऐप्लिकेशन ये काम करेगा:

  • वेब कॉन्टेंट को WebView में दिखाएं
  • WebView के ऊपर स्टैक किए गए Flutter विजेट दिखाएं
  • पेज लोड होने के इवेंट पर प्रतिक्रिया दें
  • WebViewController की मदद से WebView को कंट्रोल करें
  • NavigationDelegate का इस्तेमाल करके वेबसाइटों को ब्लॉक करें
  • JavaScript एक्सप्रेशन का आकलन करना
  • JavascriptChannels की मदद से JavaScript से कॉलबैक मैनेज करना
  • कुकी सेट करना, हटाना, जोड़ना या दिखाना
  • एचटीएमएल वाली ऐसेट, फ़ाइलों या स्ट्रिंग से एचटीएमएल लोड करें और दिखाएं

iPhone सिम्युलेटर का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. इस इमेज में एम्बेड किया गया वेबव्यू, Flutter.dev का होम पेज दिखा रहा है

Android एम्युलेटर का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन चल रहा है. साथ ही, एम्बेड किया गया ऐसा वेबव्यू दिखाया गया है जिसमें Flutter.dev का होम पेज दिख रहा है

आपको इनके बारे में जानकारी मिलेगी

इस कोडलैब में, आप webview_flutter प्लगिन को कई तरीकों से इस्तेमाल करने के बारे में जानेंगे. इनमें ये शामिल हैं:

  • webview_flutter प्लगिन को कॉन्फ़िगर करने का तरीका
  • पेज लोड होने की स्थिति से जुड़े इवेंट का पता लगाने का तरीका
  • पेज नेविगेशन को कंट्रोल करने का तरीका
  • WebView को उसके इतिहास में, पीछे और आगे जाने का निर्देश कैसे दें
  • JavaScript का आकलन करने का तरीका. इसमें लौटाए गए नतीजों को इस्तेमाल करने का तरीका भी शामिल है
  • JavaScript से Dart कोड को कॉल करने के लिए कॉलबैक रजिस्टर करने का तरीका
  • कुकी कैसे प्रबंधित करें
  • ऐसेट, फ़ाइलों या एचटीएमएल वाली स्ट्रिंग से एचटीएमएल पेज लोड करने और दिखाने का तरीका

आपको इनकी ज़रूरत होगी

2. Flutter डेवलपमेंट एनवायरमेंट को सेट अप करें

इस लैब को पूरा करने के लिए, आपको सॉफ़्टवेयर के दो हिस्सों की ज़रूरत होगी—Flutter SDK टूल और एडिटर.

इनमें से किसी भी डिवाइस का इस्तेमाल करके, कोडलैब चलाया जा सकता है:

  • आपके कंप्यूटर से कनेक्ट किया गया Android या iOS डिवाइस होना चाहिए और वह डेवलपर मोड पर सेट होना चाहिए.
  • iOS सिम्युलेटर (Xcode टूल इंस्टॉल करना ज़रूरी है).
  • Android Emulator (Android Studio में सेटअप करना ज़रूरी है).

3. शुरू करना

Flutter का इस्तेमाल करना

Flutter का नया प्रोजेक्ट बनाने के कई तरीके हैं. Android Studio और विज़ुअल Studio कोड, दोनों ही इस टास्क के लिए टूल उपलब्ध कराते हैं. प्रोजेक्ट बनाने के लिए, लिंक की गई प्रोसेस अपनाएं या आसान कमांड लाइन टर्मिनल में यहां दिए गए निर्देशों को एक्ज़ीक्यूट करें.

$ flutter create --platforms=android,ios webview_in_flutter
Creating project webview_in_flutter...
Resolving dependencies in `webview_in_flutter`... 
Downloading packages... 
Got dependencies in `webview_in_flutter`.
Wrote 74 files.

All done!
You can find general documentation for Flutter at: https://docs.flutter.dev/
Detailed API documentation is available at: https://api.flutter.dev/
If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev

In order to run your application, type:

  $ cd webview_in_flutter
  $ flutter run

Your application code is in webview_in_flutter/lib/main.dart.

डिपेंडेंसी के तौर पर WebView Flutter प्लगिन जोड़ना

Pub पैकेज का इस्तेमाल करके, Flutter ऐप्लिकेशन में अतिरिक्त सुविधाएं जोड़ना आसान है. इस कोडलैब में, आपको अपने प्रोजेक्ट में webview_flutter प्लगिन जोड़ना होगा. टर्मिनल में इन निर्देशों को चलाएं.

$ cd webview_in_flutter
$ flutter pub add webview_flutter
Resolving dependencies... 
Downloading packages... 
  leak_tracker 10.0.4 (10.0.5 available)
  leak_tracker_flutter_testing 3.0.3 (3.0.5 available)
  material_color_utilities 0.8.0 (0.11.1 available)
  meta 1.12.0 (1.14.0 available)
+ plugin_platform_interface 2.1.8
  test_api 0.7.0 (0.7.1 available)
+ webview_flutter 4.7.0
+ webview_flutter_android 3.16.0
+ webview_flutter_platform_interface 2.10.0
+ webview_flutter_wkwebview 3.13.0
Changed 5 dependencies!
5 packages have newer versions incompatible with dependency constraints.
Try `flutter pub outdated` for more information.

अपने pubspec.yaml की जांच करने पर, आपको webview_flutter प्लगिन के डिपेंडेंसी सेक्शन में एक लाइन दिखेगी.

Android minSDK को कॉन्फ़िगर करना

Android पर webview_flutter प्लगिन का इस्तेमाल करने के लिए, आपको minSDK को 20 पर सेट करना होगा. अपनी android/app/build.gradle फ़ाइल में इस तरह से बदलाव करें:

android/app/build.gradle

android {
    //...

    defaultConfig {
        applicationId = "com.example.webview_in_flutter"
        minSdk = 20                                         // Modify this line
        targetSdk = flutter.targetSdkVersion
        versionCode = flutterVersionCode.toInteger()
        versionName = flutterVersionName
    }

4. Flutter ऐप्लिकेशन में वेबव्यू विजेट जोड़ना

इस चरण में आपको अपने आवेदन में एक WebView जोड़ना होगा. वेबव्यू, नेटिव व्यू के तौर पर होस्ट किए जाते हैं. ऐप्लिकेशन डेवलपर के तौर पर, आपके पास यह तय करने का विकल्प होता है कि अपने ऐप्लिकेशन में इन नेटिव व्यू को कैसे होस्ट किया जा सकता है. Android पर, वर्चुअल डिसप्ले और हाइब्रिड कंपोज़िशन, दोनों में से किसी एक को चुना जा सकता है. फ़िलहाल, यह Android के लिए डिफ़ॉल्ट तौर पर उपलब्ध है. हालांकि, iOS हमेशा हाइब्रिड कंपोज़िशन का इस्तेमाल करता है.

वर्चुअल डिसप्ले और हाइब्रिड कंपोज़िशन के बीच के अंतर के बारे में गहराई से जानने के लिए, कृपया प्लैटफ़ॉर्म व्यू की मदद से अपने Flutter ऐप्लिकेशन में Android और iOS व्यू को होस्ट करना लेख पढ़ें.

स्क्रीन पर वेबव्यू डालना

lib/main.dart की सामग्री को निम्न से बदलें:

lib/main.dart

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

void main() {
  runApp(
    MaterialApp(
      theme: ThemeData(useMaterial3: true),
      home: const WebViewApp(),
    ),
  );
}

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

  @override
  State<WebViewApp> createState() => _WebViewAppState();
}

class _WebViewAppState extends State<WebViewApp> {
  late final WebViewController controller;

  @override
  void initState() {
    super.initState();
    controller = WebViewController()
      ..loadRequest(
        Uri.parse('https://flutter.dev'),
      );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView'),
      ),
      body: WebViewWidget(
        controller: controller,
      ),
    );
  }
}

iOS या Android पर इसे चलाने पर, आपके डिवाइस पर किसी वेबव्यू को फ़ुल ब्लीड ब्राउज़र विंडो के तौर पर दिखाया जाएगा. इसका मतलब है कि ब्राउज़र आपके डिवाइस पर बिना किसी बॉर्डर या मार्जिन के फ़ुलस्क्रीन में दिखता है. स्क्राेल करने पर आपको पेज के कुछ ऐसे हिस्से दिखेंगे जो थोड़े अजीब लग सकते हैं. इसकी वजह यह है कि फ़िलहाल JavaScript बंद है और flutter.dev को रेंडर करने के लिए JavaScript की ज़रूरत है.

ऐप्लिकेशन चलाना

iOS या Android पर Flutter ऐप्लिकेशन इस्तेमाल करके वेबव्यू देखें. इसमें flutter.dev वेबसाइट दिखती है. इसके अलावा, ऐप्लिकेशन को Android Emulator या iOS सिम्युलेटर में भी चलाया जा सकता है. आप चाहें, तो शुरुआती वेबव्यू के यूआरएल को अपनी वेबसाइट के यूआरएल से बदल दें.

$ flutter run

यह मानते हुए कि आपके डिवाइस पर ऐप्लिकेशन को कंपाइल और डिप्लॉय करने के बाद, आपके पास सही सिम्युलेटर या एम्युलेटर चल रहा है या कोई फ़िज़िकल डिवाइस मौजूद है, आपको कुछ ऐसा दिखेगा:

iPhone सिम्युलेटर का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. इस इमेज में एम्बेड किया गया वेबव्यू, Flutter.dev का होम पेज दिखा रहा है

Android एम्युलेटर का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन चल रहा है. साथ ही, एम्बेड किया गया ऐसा वेबव्यू दिखाया गया है जिसमें Flutter.dev का होम पेज दिख रहा है

5. पेज लोड इवेंट के बारे में सुना जा रहा है

WebView विजेट की मदद से, पेज लोड होने की प्रोग्रेस से जुड़े कई इवेंट मिलते हैं. इन्हें आपका ऐप्लिकेशन सुन सकता है. WebView पेज लोड साइकल के दौरान, तीन अलग-अलग पेज लोड इवेंट ट्रिगर होते हैं: onPageStarted, onProgress, और onPageFinished. इस चरण में, आपको पेज लोड इंडिकेटर लागू करना होगा. बोनस के तौर पर, इससे यह पता चलेगा कि WebView के कॉन्टेंट एरिया में Flutter कॉन्टेंट को रेंडर किया जा सकता है.

अपने ऐप्लिकेशन में पेज लोड इवेंट जोड़ना

lib/src/web_view_stack.dart पर एक नई सोर्स फ़ाइल बनाएं और उसमें यह कॉन्टेंट डालें:

lib/src/web_view_stack.dart

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;
  late final WebViewController controller;

  @override
  void initState() {
    super.initState();
    controller = WebViewController()
      ..setNavigationDelegate(NavigationDelegate(
        onPageStarted: (url) {
          setState(() {
            loadingPercentage = 0;
          });
        },
        onProgress: (progress) {
          setState(() {
            loadingPercentage = progress;
          });
        },
        onPageFinished: (url) {
          setState(() {
            loadingPercentage = 100;
          });
        },
      ))
      ..loadRequest(
        Uri.parse('https://flutter.dev'),
      );
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebViewWidget(
          controller: controller,
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }
}

इस कोड ने WebView विजेट को Stack में रैप कर दिया है. पेज लोड प्रतिशत 100% से कम होने पर, यह WebView को शर्तों के साथ LinearProgressIndicator से ओवरले कर देता है. इसमें प्रोग्राम की ऐसी स्थिति शामिल है जो समय के साथ बदलती रहती है. इसलिए, आपने इस स्थिति को StatefulWidget से जुड़ी State क्लास में सेव किया है.

इस नए WebViewStack विजेट का इस्तेमाल करने के लिए, अपने lib/main.dart को में इस तरह बदलाव करें:

lib/main.dart

import 'package:flutter/material.dart';

import 'src/web_view_stack.dart';

void main() {
  runApp(
    MaterialApp(
      theme: ThemeData(useMaterial3: true),
      home: const WebViewApp(),
    ),
  );
}

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

  @override
  State<WebViewApp> createState() => _WebViewAppState();
}

class _WebViewAppState extends State<WebViewApp> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView'),
      ),
      body: const WebViewStack(),
    );
  }
}

जब आपके नेटवर्क की स्थितियों और ब्राउज़र ने उस पेज को कैश मेमोरी में सेव किया है जिस पर आप नेविगेट कर रहे हैं, के आधार पर ऐप्लिकेशन चलाते समय आपको WebView कॉन्टेंट एरिया के ऊपर एक पेज लोड होने का संकेत दिखेगा.

6. WebViewController के साथ काम करना

WebView विजेट से WebViewController को ऐक्सेस करना

WebView विजेट की मदद से, WebViewController की मदद से प्रोग्राम के हिसाब से कंट्रोल किया जा सकता है. यह नियंत्रक, कॉलबैक के ज़रिए WebView विजेट बनाने के बाद उपलब्ध होता है. इस कंट्रोलर की उपलब्धता एसिंक्रोनस होने की वजह से, यह Dart की एसिंक्रोनस Completer<T> क्लास के लिए मुख्य उम्मीदवार है.

lib/src/web_view_stack.dart को इस तरह अपडेट करें:

lib/src/web_view_stack.dart

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

class WebViewStack extends StatefulWidget {
  const WebViewStack({required this.controller, super.key}); // MODIFY

  final WebViewController controller;                        // ADD

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

class _WebViewStackState extends State<WebViewStack> {
  var loadingPercentage = 0;
  // REMOVE the controller that was here

  @override
  void initState() {
    super.initState();
    // Modify from here...
    widget.controller.setNavigationDelegate(
      NavigationDelegate(
        onPageStarted: (url) {
          setState(() {
            loadingPercentage = 0;
          });
        },
        onProgress: (progress) {
          setState(() {
            loadingPercentage = progress;
          });
        },
        onPageFinished: (url) {
          setState(() {
            loadingPercentage = 100;
          });
        },
      ),
    );
    // ...to here.
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebViewWidget(
          controller: widget.controller,                     // MODIFY
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }
}

WebViewStack विजेट अब आस-पास के विजेट में बनाए गए कंट्रोलर का इस्तेमाल करता है. इससे WebViewWidget का कंट्रोलर, ऐप्लिकेशन के अन्य हिस्सों के साथ आसानी से शेयर हो जाएगा.

नेविगेशन कंट्रोल बनाना

WebView का काम करना एक बात है, लेकिन पेज इतिहास को देखकर आगे और पीछे जाने के साथ-साथ, पेज को फिर से लोड करना भी काफ़ी मददगार साबित हो सकता है. अच्छी बात यह है कि WebViewController की मदद से, इस सुविधा को अपने ऐप्लिकेशन में जोड़ा जा सकता है.

lib/src/navigation_controls.dart पर एक नई सोर्स फ़ाइल बनाएं और उसमें यह जानकारी दें:

lib/src/navigation_controls.dart

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

class NavigationControls extends StatelessWidget {
  const NavigationControls({required this.controller, super.key});

  final WebViewController controller;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        IconButton(
          icon: const Icon(Icons.arrow_back_ios),
          onPressed: () async {
            final messenger = ScaffoldMessenger.of(context);
            if (await controller.canGoBack()) {
              await controller.goBack();
            } else {
              messenger.showSnackBar(
                const SnackBar(content: Text('No back history item')),
              );
              return;
            }
          },
        ),
        IconButton(
          icon: const Icon(Icons.arrow_forward_ios),
          onPressed: () async {
            final messenger = ScaffoldMessenger.of(context);
            if (await controller.canGoForward()) {
              await controller.goForward();
            } else {
              messenger.showSnackBar(
                const SnackBar(content: Text('No forward history item')),
              );
              return;
            }
          },
        ),
        IconButton(
          icon: const Icon(Icons.replay),
          onPressed: () {
            controller.reload();
          },
        ),
      ],
    );
  }
}

यह विजेट, कंस्ट्रक्शन के समय इसके साथ शेयर किए गए WebViewController का इस्तेमाल करता है, ताकि उपयोगकर्ता IconButton की सीरीज़ के ज़रिए WebView को कंट्रोल कर सके.

AppBar में नेविगेशन कंट्रोल जोड़ना

अपडेट किए गए WebViewStack और हाल ही में तैयार किए गए NavigationControls के साथ, अब अपडेट किए गए WebViewApp में इन सभी चीज़ों को एक साथ रखने का समय आ गया है. हम यहां शेयर किए गए WebViewController को बनाते हैं. ऐप्लिकेशन में विजेट ट्री के ऊपरी हिस्से के पास WebViewApp होने की वजह से, इसे इस लेवल पर बनाना सही होता है.

lib/main.dart फ़ाइल को इस तरह अपडेट करें:

lib/main.dart

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

import 'src/navigation_controls.dart';                  // ADD
import 'src/web_view_stack.dart';

void main() {
  runApp(
    MaterialApp(
      theme: ThemeData(useMaterial3: true),
      home: const WebViewApp(),
    ),
  );
}

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

  @override
  State<WebViewApp> createState() => _WebViewAppState();
}

class _WebViewAppState extends State<WebViewApp> {
  // Add from here...
  late final WebViewController controller;

  @override
  void initState() {
    super.initState();
    controller = WebViewController()
      ..loadRequest(
        Uri.parse('https://flutter.dev'),
      );
  }
  // ...to here.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView'),
        // Add from here...
        actions: [
          NavigationControls(controller: controller),
        ],
        // ...to here.
      ),
      body: WebViewStack(controller: controller),       // MODIFY
    );
  }
}

ऐप्लिकेशन चलाने पर, कंट्रोल वाला वेब पेज दिखेगा:

iPhone सिम्युलेटर का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन दिख रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है. इसमें पिछले पेज, अगले पेज, और पेज को फिर से लोड करने के कंट्रोल के साथ Flutter.dev का होम पेज दिख रहा है

Android Emulator का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है. इसमें पिछले पेज, अगले पेज, और पेज को फिर से लोड करने से जुड़े कंट्रोल के साथ Flutter.dev का होम पेज दिख रहा है

7. नेविगेशन का इस्तेमाल करके नेविगेशन पर नज़र रखना

WebView, आपके ऐप्लिकेशन को NavigationDelegate, उपलब्ध कराता है. इससे आपका ऐप्लिकेशन, WebView विजेट के पेज नेविगेशन को ट्रैक और कंट्रोल कर पाता है. जब WebView, से नेविगेशन शुरू किया जाता है, जैसे कि जब कोई उपयोगकर्ता किसी लिंक पर क्लिक करता है, तो NavigationDelegate को कॉल किया जाता है. NavigationDelegate कॉलबैक का इस्तेमाल करके, यह कंट्रोल किया जा सकता है कि WebView नेविगेशन के साथ आगे बढ़ें या नहीं.

कस्टम नेविगेशन प्रतिनिधि को रजिस्टर करना

इस चरण में, YouTube.com पर नेविगेशन को ब्लॉक करने के लिए, NavigationDelegate कॉलबैक को रजिस्टर किया जाएगा. ध्यान दें कि इसे आसान तरीके से लागू करने पर, YouTube पर इनलाइन कॉन्टेंट को भी ब्लॉक किया जा सकता है. यह कॉन्टेंट, Flutter API के दस्तावेज़ वाले पेजों पर दिखता है.

lib/src/web_view_stack.dart को नीचे बताए गए तरीके से अपडेट करें:

lib/src/web_view_stack.dart

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

class WebViewStack extends StatefulWidget {
  const WebViewStack({required this.controller, super.key});

  final WebViewController controller;

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

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

  @override
  void initState() {
    super.initState();
    widget.controller.setNavigationDelegate(
      NavigationDelegate(
        onPageStarted: (url) {
          setState(() {
            loadingPercentage = 0;
          });
        },
        onProgress: (progress) {
          setState(() {
            loadingPercentage = progress;
          });
        },
        onPageFinished: (url) {
          setState(() {
            loadingPercentage = 100;
          });
        },
        // Add from here...
        onNavigationRequest: (navigation) {
          final host = Uri.parse(navigation.url).host;
          if (host.contains('youtube.com')) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text(
                  'Blocking navigation to $host',
                ),
              ),
            );
            return NavigationDecision.prevent;
          }
          return NavigationDecision.navigate;
        },
        // ...to here.
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebViewWidget(
          controller: widget.controller,
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }
}

अगले चरण में, आपको WebViewController क्लास का इस्तेमाल करके, NavigationDelegate को टेस्ट करने के लिए एक मेन्यू आइटम जोड़ना होगा. इसे पढ़ने वाले लोगों के लिए एक अभ्यास के तौर पर छोड़ दिया जाता है कि कॉलबैक के लॉजिक को बेहतर बनाया जा सके, ताकि सिर्फ़ YouTube.com पर पूरे पेज के लिए नेविगेशन को ब्लॉक किया जा सके. साथ ही, एपीआई दस्तावेज़ में इनलाइन YouTube कॉन्टेंट को अनुमति दी जा सके.

8. AppBar में मेन्यू बटन जोड़ना

अगले कुछ चरणों में, आपको AppBar विजेट में एक मेन्यू बटन बनाना होगा. इसका इस्तेमाल JavaScript का आकलन करने, JavaScript चैनलों को शुरू करने, और कुकी मैनेज करने के लिए किया जाएगा. कुल मिलाकर, यह एक मददगार मेन्यू है.

lib/src/menu.dart पर एक नई सोर्स फ़ाइल बनाएं और उसमें यह जानकारी दें:

lib/src/menu.dart

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

enum _MenuOptions {
  navigationDelegate,
}

class Menu extends StatelessWidget {
  const Menu({required this.controller, super.key});

  final WebViewController controller;

  @override
  Widget build(BuildContext context) {
    return PopupMenuButton<_MenuOptions>(
      onSelected: (value) async {
        switch (value) {
          case _MenuOptions.navigationDelegate:
            await controller.loadRequest(Uri.parse('https://youtube.com'));
        }
      },
      itemBuilder: (context) => [
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.navigationDelegate,
          child: Text('Navigate to YouTube'),
        ),
      ],
    );
  }
}

जब लोग YouTube पर जाएं मेन्यू का विकल्प चुनते हैं, तब WebViewController का loadRequest तरीका इस्तेमाल किया जाता है. यह नेविगेशन, पिछले चरण में बनाए गए navigationDelegate कॉलबैक से ब्लॉक हो जाएगा.

WebViewApp की स्क्रीन पर मेन्यू जोड़ने के लिए, lib/main.dart में इस तरीके का इस्तेमाल करें:

lib/main.dart

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

import 'src/menu.dart';                               // ADD
import 'src/navigation_controls.dart';
import 'src/web_view_stack.dart';

void main() {
  runApp(
    MaterialApp(
      theme: ThemeData(useMaterial3: true),
      home: const WebViewApp(),
    ),
  );
}

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

  @override
  State<WebViewApp> createState() => _WebViewAppState();
}

class _WebViewAppState extends State<WebViewApp> {
  late final WebViewController controller;

  @override
  void initState() {
    super.initState();
    controller = WebViewController()
      ..loadRequest(
        Uri.parse('https://flutter.dev'),
      );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView'),
        actions: [
          NavigationControls(controller: controller),
          Menu(controller: controller),               // ADD
        ],
      ),
      body: WebViewStack(controller: controller),
    );
  }
}

अपना ऐप्लिकेशन चलाएं और YouTube पर जाएं मेन्यू आइटम पर टैप करें. आपका स्वागत करने के लिए, SnackBar से आपको यह जानकारी मिलेगी कि नेविगेशन कंट्रोलर ने YouTube पर नेविगेट करने की सुविधा को ब्लॉक कर दिया है.

Android Emulator का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है. इसमें Flutter.dev का होम पेज दिख रहा है. साथ ही, मेन्यू आइटम के साथ &#39;YouTube पर नेविगेट करें&#39; विकल्प दिख रहा है

Android Emulator का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है. इसमें Flutter.dev का होम पेज दिख रहा है. साथ ही, उसमें टोस्ट पॉप-अप दिख रहा है, जिस पर &#39;m.youtube.com पर नेविगेशन को ब्लॉक किया जा रहा है&#39; लिखा हुआ है

9. JavaScript का मूल्यांकन करना

WebViewController, मौजूदा पेज के संदर्भ में JavaScript एक्सप्रेशन का आकलन कर सकता है. JavaScript का आकलन करने के दो अलग-अलग तरीके हैं: वैल्यू न दिखाने वाले JavaScript कोड के लिए, runJavaScript का इस्तेमाल करें. वैल्यू दिखाने वाले JavaScript कोड के लिए, runJavaScriptReturningResult का इस्तेमाल करें.

JavaScript चालू करने के लिए, आपको WebViewController को कॉन्फ़िगर करना होगा, जिसमें javaScriptMode प्रॉपर्टी को JavascriptMode.unrestricted पर सेट किया गया हो. डिफ़ॉल्ट रूप से, javascriptMode को JavascriptMode.disabled पर सेट किया जाता है.

javascriptMode सेटिंग को इस तरह से जोड़कर, _WebViewStackState क्लास को अपडेट करें:

lib/src/web_view_stack.dart

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

  @override
  void initState() {
    super.initState();
    widget.controller
      ..setNavigationDelegate(              // Modify this line to use .. instead of .
        NavigationDelegate(
          onPageStarted: (url) {
            setState(() {
              loadingPercentage = 0;
            });
          },
          onProgress: (progress) {
            setState(() {
              loadingPercentage = progress;
            });
          },
          onPageFinished: (url) {
            setState(() {
              loadingPercentage = 100;
            });
          },
          onNavigationRequest: (navigation) {
            final host = Uri.parse(navigation.url).host;
            if (host.contains('youtube.com')) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text(
                    'Blocking navigation to $host',
                  ),
                ),
              );
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
        ),
      )
      ..setJavaScriptMode(JavaScriptMode.unrestricted);        // Add this line
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebViewWidget(
          controller: widget.controller,
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }
}

अब WebViewWidget से JavaScript चलाया जा सकता है. इसलिए, अब runJavaScriptReturningResult तरीके का इस्तेमाल करने के लिए मेन्यू में कोई विकल्प जोड़ा जा सकता है.

अपने Editor या कुछ कीबोर्ड का इस्तेमाल करके, मेन्यू क्लास को StatefulWidget में बदलें. lib/src/menu.dart में बदलाव करें, ताकि वह इससे मिलता-जुलता हो:

lib/src/menu.dart

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

enum _MenuOptions {
  navigationDelegate,
  userAgent,                                              // Add this line
}

class Menu extends StatefulWidget {                       // Convert to StatefulWidget
  const Menu({required this.controller, super.key});

  final WebViewController controller;

  @override                                               // Add from here
  State<Menu> createState() => _MenuState();
}

class _MenuState extends State<Menu> {                    // To here.
  @override
  Widget build(BuildContext context) {
    return PopupMenuButton<_MenuOptions>(
      onSelected: (value) async {
        switch (value) {
          case _MenuOptions.navigationDelegate:           // Modify from here
            await widget.controller
                .loadRequest(Uri.parse('https://youtube.com'));
          case _MenuOptions.userAgent:
            final userAgent = await widget.controller
                .runJavaScriptReturningResult('navigator.userAgent');
            if (!context.mounted) return;
            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
              content: Text('$userAgent'),
            ));                                           // To here.
        }
      },
      itemBuilder: (context) => [
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.navigationDelegate,
          child: Text('Navigate to YouTube'),
        ),
        const PopupMenuItem<_MenuOptions>(                // Add from here
          value: _MenuOptions.userAgent,
          child: Text('Show user-agent'),
        ),                                                // To here.
      ],
    );
  }
}

जब आप 'उपयोगकर्ता एजेंट दिखाएं' पर टैप करें मेन्यू विकल्प के तौर पर, JavaScript एक्सप्रेशन navigator.userAgent को चलाने का नतीजा Snackbar में दिखाया जाता है. ऐप्लिकेशन चलाते समय, आपको यह दिख सकता है कि Flutter.dev पेज अलग दिख रहा है. यह JavaScript को चालू रखने का नतीजा है.

iPhone के सिम्युलेटर का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन दिख रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है. इसमें Flutter.dev का होम पेज दिख रहा है. साथ ही, मेन्यू आइटम के साथ &#39;YouTube पर नेविगेट करें&#39; विकल्प दिख रहा है या &#39;उपयोगकर्ता-एजेंट दिखाएं&#39;

iPhone के सिम्युलेटर का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन दिख रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है. इसमें Flutter.dev का होम पेज दिख रहा है. साथ ही, टोस्ट का पॉप-अप दिख रहा है, जिसमें उपयोगकर्ता एजेंट स्ट्रिंग दिख रही है.

10. JavaScript चैनलों का इस्तेमाल करना

JavaScript चैनल की मदद से, आपका ऐप्लिकेशन WebViewWidget के JavaScript कॉन्टेक्स्ट में कॉलबैक हैंडलर को रजिस्टर करता है. इसका इस्तेमाल, ऐप्लिकेशन के डार्ट कोड में वैल्यू को वापस भेजने के लिए किया जा सकता है. इस चरण में आपको एक SnackBar चैनल रजिस्टर करना होगा, जिसे XMLHttpRequest के नतीजे के साथ कॉल किया जाएगा.

WebViewStack क्लास को इस तरह अपडेट करें:

lib/src/web_view_stack.dart

class WebViewStack extends StatefulWidget {
  const WebViewStack({required this.controller, super.key});

  final WebViewController controller;

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

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

  @override
  void initState() {
    super.initState();
    widget.controller
      ..setNavigationDelegate(
        NavigationDelegate(
          onPageStarted: (url) {
            setState(() {
              loadingPercentage = 0;
            });
          },
          onProgress: (progress) {
            setState(() {
              loadingPercentage = progress;
            });
          },
          onPageFinished: (url) {
            setState(() {
              loadingPercentage = 100;
            });
          },
          onNavigationRequest: (navigation) {
            final host = Uri.parse(navigation.url).host;
            if (host.contains('youtube.com')) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text(
                    'Blocking navigation to $host',
                  ),
                ),
              );
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
        ),
      )
      // Modify from here...
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..addJavaScriptChannel(
        'SnackBar',
        onMessageReceived: (message) {
          ScaffoldMessenger.of(context)
              .showSnackBar(SnackBar(content: Text(message.message)));
        },
      );
      // ...to here.
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebViewWidget(
          controller: widget.controller,
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }
}

Set में हर JavaScript चैनल के लिए, चैनल ऑब्जेक्ट को JavaScript चैनल name के नाम से ही एक विंडो प्रॉपर्टी के रूप में उपलब्ध कराया जाता है. JavaScript कॉन्टेक्स्ट से इसका इस्तेमाल करने का मतलब है कि JavaScript चैनल पर postMessage को कॉल करके मैसेज भेजा जाए. यह मैसेज JavascriptChannel के onMessageReceived कॉलबैक हैंडलर को भेजा जाता है.

ऊपर जोड़े गए JavaScript चैनल का इस्तेमाल करने के लिए, एक और मेन्यू आइटम जोड़ें. यह मेन्यू आइटम, JavaScript के कॉन्टेक्स्ट में XMLHttpRequest लागू करता है और SnackBar JavaScript चैनल का इस्तेमाल करके, नतीजों को वापस भेजता है.

अब जब WebViewWidget को हमारे JavaScript चैनलों के बारे में जानकारी है,, तो अब आपको ऐप्लिकेशन का दायरा बढ़ाने के लिए उदाहरण जोड़ना होगा. ऐसा करने के लिए, Menu क्लास में एक और PopupMenuItem जोड़ें. साथ ही, अतिरिक्त फ़ंक्शन जोड़ें.

javascriptChannel की इन्यूमरेशन वैल्यू जोड़कर, ज़्यादा मेन्यू विकल्प के साथ _MenuOptions को अपडेट करें. इसके बाद, Menu क्लास में इस तरह से लागू करें:

lib/src/menu.dart

enum _MenuOptions {
  navigationDelegate,
  userAgent,
  javascriptChannel,                                      // Add this option
}

class Menu extends StatefulWidget {
  const Menu({required this.controller, super.key});

  final WebViewController controller;

  @override
  State<Menu> createState() => _MenuState();
}

class _MenuState extends State<Menu> {
  @override
  Widget build(BuildContext context) {
    return PopupMenuButton<_MenuOptions>(
      onSelected: (value) async {
        switch (value) {
          case _MenuOptions.navigationDelegate:
            await widget.controller
                .loadRequest(Uri.parse('https://youtube.com'));
          case _MenuOptions.userAgent:
            final userAgent = await widget.controller
                .runJavaScriptReturningResult('navigator.userAgent');
            if (!context.mounted) return;
            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
              content: Text('$userAgent'),
            ));
          case _MenuOptions.javascriptChannel:            // Add from here
            await widget.controller.runJavaScript('''
var req = new XMLHttpRequest();
req.open('GET', "https://api.ipify.org/?format=json");
req.onload = function() {
  if (req.status == 200) {
    let response = JSON.parse(req.responseText);
    SnackBar.postMessage("IP Address: " + response.ip);
  } else {
    SnackBar.postMessage("Error: " + req.status);
  }
}
req.send();''');                                          // To here.
        }
      },
      itemBuilder: (context) => [
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.navigationDelegate,
          child: Text('Navigate to YouTube'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.userAgent,
          child: Text('Show user-agent'),
        ),
        const PopupMenuItem<_MenuOptions>(                // Add from here
          value: _MenuOptions.javascriptChannel,
          child: Text('Lookup IP Address'),
        ),                                                // To here.
      ],
    );
  }
}

यह JavaScript तब लागू होता है, जब उपयोगकर्ता JavaScript चैनल का उदाहरण मेन्यू का विकल्प चुनता है.

var req = new XMLHttpRequest();
req.open('GET', "https://api.ipify.org/?format=json");
req.onload = function() {
  if (req.status == 200) {
    SnackBar.postMessage(req.responseText);
  } else {
    SnackBar.postMessage("Error: " + req.status);
  }
}
req.send();

यह कोड, Public IP Address API को GET अनुरोध भेजता है. इससे डिवाइस का आईपी पता दिखता है. SnackBar JavascriptChannel पर postMessage का इस्तेमाल करने से, यह नतीजा SnackBar में दिखाया जाता है.

11. कुकी मैनेज करना

आपका ऐप्लिकेशन, CookieManager क्लास का इस्तेमाल करके WebView में कुकी मैनेज कर सकता है. इस चरण में, आपको कुकी की सूची दिखानी होगी, कुकी की सूची मिटानी होगी, कुकी को मिटाना होगा, और नई कुकी सेट करनी होंगी. हर कुकी के इस्तेमाल के उदाहरण के लिए, _MenuOptions में इस तरह एंट्री जोड़ें:

lib/src/menu.dart

enum _MenuOptions {
  navigationDelegate,
  userAgent,
  javascriptChannel,
  // Add from here ...
  listCookies,
  clearCookies,
  addCookie,
  setCookie,
  removeCookie,
  // ... to here.
}

इस चरण में किए गए बाकी बदलावों का फ़ोकस Menu क्लास पर होता है. इसमें, Menu क्लास को स्टेटलेस से स्टेटफ़ुल में बदलना भी शामिल है. यह बदलाव ज़रूरी है, क्योंकि Menu के पास CookieManager का मालिकाना हक होना चाहिए. साथ ही, स्टेटलेस विजेट की नहीं बदली जा सकने वाली स्थिति, खराब कॉम्बिनेशन है.

CookieManager को, नतीजे मिलने वाली स्टेट क्लास में इस तरह जोड़ें:

lib/src/menu.dart

class Menu extends StatefulWidget {
  const Menu({required this.controller, super.key});

  final WebViewController controller;

  @override
  State<Menu> createState() => _MenuState();
}

class _MenuState extends State<Menu> {
  final cookieManager = WebViewCookieManager();       // Add this line

  @override
  Widget build(BuildContext context) {
  // ...

_MenuState क्लास में वह कोड शामिल होगा जिसे पहले Menu क्लास में जोड़ा गया था. साथ ही, उसमें CookieManager जोड़ी गई नई क्लास भी शामिल होगी. सेक्शन की अगली सीरीज़ में, आपको _MenuState में हेल्पर फ़ंक्शन जोड़ना है. बदले में, जोड़े गए मेन्यू आइटम के ज़रिए उन्हें शुरू किया जाएगा.

सभी कुकी की सूची पाना

सभी कुकी की सूची पाने के लिए, JavaScript का इस्तेमाल किया जाएगा. इसे पाने के लिए, _MenuState क्लास के आखिर में _onListCookies नाम का एक हेल्पर तरीका जोड़ें. runJavaScriptReturningResult तरीके का इस्तेमाल करके, आपका हेल्पर तरीका JavaScript के कॉन्टेक्स्ट में document.cookie को एक्ज़ीक्यूट करता है. इससे सभी कुकी की सूची दिखती है.

_MenuState क्लास में यह जोड़ें:

lib/src/menu.dart

Future<void> _onListCookies(WebViewController controller) async {
  final String cookies = await controller
      .runJavaScriptReturningResult('document.cookie') as String;
  if (!mounted) return;
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Text(cookies.isNotEmpty ? cookies : 'There are no cookies.'),
    ),
  );
}

सभी कुकी मिटाना

वेबव्यू में सभी कुकी मिटाने के लिए, CookieManager क्लास का clearCookies तरीका इस्तेमाल करें. यह तरीका Future<bool> दिखाता है, जिसका समाधान CookieManager कुकी को हटाने पर true के रूप में होता है. साथ ही, अगर हटाने के लिए कोई कुकी नहीं थी, तो false के नतीजे दिखाता है.

_MenuState क्लास में यह जोड़ें:

lib/src/menu.dart

Future<void> _onClearCookies() async {
  final hadCookies = await cookieManager.clearCookies();
  String message = 'There were cookies. Now, they are gone!';
  if (!hadCookies) {
    message = 'There were no cookies to clear.';
  }
  if (!mounted) return;
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Text(message),
    ),
  );
}

JavaScript को चालू करके कुकी जोड़ी जा सकती है. कुकी को JavaScript दस्तावेज़ में जोड़ने के लिए जिस एपीआई का इस्तेमाल किया जाता है उसे MDN पर पूरी जानकारी के साथ दर्ज किया जाता है.

_MenuState क्लास में यह जोड़ें:

lib/src/menu.dart

Future<void> _onAddCookie(WebViewController controller) async {
  await controller.runJavaScript('''var date = new Date();
  date.setTime(date.getTime()+(30*24*60*60*1000));
  document.cookie = "FirstName=John; expires=" + date.toGMTString();''');
  if (!mounted) return;
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('Custom cookie added.'),
    ),
  );
}

CookieManager का इस्तेमाल करके, भी कुकी सेट की जा सकती हैं. इसके लिए, यह तरीका अपनाएं.

_MenuState क्लास में यह जोड़ें:

lib/src/menu.dart

Future<void> _onSetCookie(WebViewController controller) async {
  await cookieManager.setCookie(
    const WebViewCookie(name: 'foo', value: 'bar', domain: 'flutter.dev'),
  );
  if (!mounted) return;
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('Custom cookie is set.'),
    ),
  );
}

कुकी को हटाने का मतलब एक कुकी जोड़ना है, जिसकी समयसीमा खत्म होने की तारीख बीत चुकी है.

_MenuState क्लास में यह जोड़ें:

lib/src/menu.dart

Future<void> _onRemoveCookie(WebViewController controller) async {
  await controller.runJavaScript(
      'document.cookie="FirstName=John; expires=Thu, 01 Jan 1970 00:00:00 UTC" ');
  if (!mounted) return;
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('Custom cookie removed.'),
    ),
  );
}

CookieManager मेन्यू आइटम जोड़ना

बस मेन्यू के विकल्पों को जोड़ना और उन्हें उन हेल्पर तरीकों से जोड़ना जो आपने अभी-अभी जोड़े हैं. _MenuState क्लास को इस तरह अपडेट करें:

lib/src/menu.dart

class _MenuState extends State<Menu> {
  final cookieManager = WebViewCookieManager();

  @override
  Widget build(BuildContext context) {
    return PopupMenuButton<_MenuOptions>(
      onSelected: (value) async {
        switch (value) {
          case _MenuOptions.navigationDelegate:
            await widget.controller
                .loadRequest(Uri.parse('https://youtube.com'));
          case _MenuOptions.userAgent:
            final userAgent = await widget.controller
                .runJavaScriptReturningResult('navigator.userAgent');
            if (!context.mounted) return;
            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
              content: Text('$userAgent'),
            ));
          case _MenuOptions.javascriptChannel:
            await widget.controller.runJavaScript('''
var req = new XMLHttpRequest();
req.open('GET', "https://api.ipify.org/?format=json");
req.onload = function() {
  if (req.status == 200) {
    let response = JSON.parse(req.responseText);
    SnackBar.postMessage("IP Address: " + response.ip);
  } else {
    SnackBar.postMessage("Error: " + req.status);
  }
}
req.send();''');
          case _MenuOptions.clearCookies:                        // Add from here
            await _onClearCookies();
          case _MenuOptions.listCookies:
            await _onListCookies(widget.controller);
          case _MenuOptions.addCookie:
            await _onAddCookie(widget.controller);
          case _MenuOptions.setCookie:
            await _onSetCookie(widget.controller);
          case _MenuOptions.removeCookie:
            await _onRemoveCookie(widget.controller);            // To here.
        }
      },
      itemBuilder: (context) => [
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.navigationDelegate,
          child: Text('Navigate to YouTube'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.userAgent,
          child: Text('Show user-agent'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.javascriptChannel,
          child: Text('Lookup IP Address'),
        ),
        const PopupMenuItem<_MenuOptions>(                       // Add from here
          value: _MenuOptions.clearCookies,
          child: Text('Clear cookies'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.listCookies,
          child: Text('List cookies'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.addCookie,
          child: Text('Add cookie'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.setCookie,
          child: Text('Set cookie'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.removeCookie,
          child: Text('Remove cookie'),
        ),                                                       // To here.
      ],
    );
  }

CookieManager का इस्तेमाल करना

ऐप्लिकेशन में अभी जोड़ी गई सभी सुविधाओं का इस्तेमाल करने के लिए, नीचे दिया गया तरीका अपनाएं:

  1. कुकी की सूची बनाएं चुनें. इसमें Google Analytics की वे कुकी शामिल होनी चाहिए जिन्हें flutter.dev ने सेट किया है.
  2. कुकी मिटाएं को चुनें. इसे रिपोर्ट करना चाहिए कि कुकी को वाकई हटा दिया गया था.
  3. कुकी मिटाएं को फिर से चुनें. इसे रिपोर्ट करना चाहिए कि मिटाने के लिए कोई भी कुकी उपलब्ध नहीं है.
  4. कुकी की सूची बनाएं चुनें. उसे रिपोर्ट करना चाहिए कि कोई कुकी नहीं है.
  5. कुकी जोड़ें को चुनें. इसे कुकी को 'जोड़ा गया' के तौर पर रिपोर्ट करना चाहिए.
  6. कुकी सेट करें को चुनें. इसे कुकी को सेट के हिसाब से रिपोर्ट करना चाहिए.
  7. कुकी की सूची बनाएं चुनें. इसके बाद, कुकी को बेहतर बनाने के लिए, कुकी हटाएं को चुनें.

Android Emulator का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है. इसमें Flutter.dev का होम पेज दिख रहा है. इसमें मेन्यू के विकल्पों की सूची है, जिसमें YouTube पर नेविगेट करने, उपयोगकर्ता एजेंट दिखाने, और ब्राउज़र के कुकी जार से इंटरैक्ट करने से जुड़ी जानकारी शामिल है

Android Emulator का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है. इसमें Flutter.dev का होम पेज दिख रहा है. साथ ही, ब्राउज़र में सेट की गई कुकी दिख रही हैं. एक टोस्ट पॉप-अप दिख रहा है

Android Emulator का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है. इसमें Flutter.dev का होम पेज दिख रहा है. साथ ही, एक टोस्ट पॉप-अप दिख रहा है, जिस पर लिखा है &#39;कुकीज़ थीं. अब वे गए नहीं!&#39;

Android एम्युलेटर का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. इस इमेज में एम्बेड किया गया वेबव्यू दिखाया गया है. इसमें Flutter.dev का होम पेज दिख रहा है. साथ ही, एक टोस्ट पॉप-अप दिख रहा है, जिस पर &#39;कस्टम कुकी जोड़ी गई&#39; लिखा है.

12. वेबव्यू में Flutter ऐसेट, फ़ाइलें, और एचटीएमएल स्ट्रिंग लोड करें

आपका ऐप्लिकेशन, अलग-अलग तरीकों का इस्तेमाल करके एचटीएमएल फ़ाइलों को लोड कर सकता है और उन्हें वेबव्यू में दिखा सकता है. इस चरण में, pubspec.yaml फ़ाइल में तय की गई Flutter ऐसेट लोड की जाएगी और तय किए गए पाथ पर मौजूद फ़ाइल को लोड किया जाएगा. साथ ही, एचटीएमएल स्ट्रिंग का इस्तेमाल करके पेज लोड किया जाएगा.

अगर आपको किसी पाथ पर मौजूद कोई फ़ाइल लोड करनी है, तो आपको pubspec.yaml में path_provider जोड़ना होगा. यह फ़ाइल सिस्टम पर, आम तौर पर इस्तेमाल की जाने वाली जगहों को ढूंढने के लिए, Flutter प्लगिन है.

कमांड लाइन पर, नीचे दिया गया कमांड चलाएं:

$ flutter pub add path_provider

एसेट लोड करने के लिए, हमें pubspec.yaml में एसेट का पाथ बताना होगा. pubspec.yaml में ये लाइनें जोड़ें:

pubspec.yaml

# The following section is specific to Flutter packages.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true
  # Add from here
  assets:
    - assets/www/index.html
    - assets/www/styles/style.css
  # ... to here.

अपने प्रोजेक्ट में ऐसेट जोड़ने के लिए, यह तरीका अपनाएं:

  1. अपने प्रोजेक्ट के रूट फ़ोल्डर में, assets नाम से एक नई डायरेक्ट्री बनाएं.
  2. assets फ़ोल्डर में www नाम वाली एक नई डायरेक्ट्री बनाएं.
  3. www फ़ोल्डर में styles नाम वाली एक नई डायरेक्ट्री बनाएं.
  4. www फ़ोल्डर में, index.html नाम से एक नई फ़ाइल बनाएं.
  5. styles फ़ोल्डर में, style.css नाम से एक नई फ़ाइल बनाएं.

इस कोड को कॉपी करके index.html फ़ाइल में चिपकाएं:

assets/www/index.html

<!DOCTYPE html>
<!-- Copyright 2013 The Flutter Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<html lang="en">
<head>
    <title>Load file or HTML string example</title>
    <link rel="stylesheet" href="styles/style.css" />
</head>
<body>

<h1>Local demo page</h1>
<p>
    This is an example page used to demonstrate how to load a local file or HTML
    string using the <a href="https://pub.dev/packages/webview_flutter">Flutter
    webview</a> plugin.
</p>

</body>
</html>

style.css के लिए एचटीएमएल हेडर स्टाइल को सेट करने के लिए इन कुछ लाइनों का इस्तेमाल करें:

assets/www/styles/style.css

h1 {
  color: blue;
}

ऐसेट सेट हो गई हैं और अब उनका इस्तेमाल किया जा सकता है. इसलिए, Flutter ऐसेट, फ़ाइलों या एचटीएमएल स्ट्रिंग को लोड करने और दिखाने के लिए ज़रूरी तरीकों को लागू किया जा सकता है.

Flutter ऐसेट लोड करें

अभी बनाई गई ऐसेट को लोड करने के लिए, आपको WebViewController का इस्तेमाल करके loadFlutterAsset तरीके को कॉल करना होगा. साथ ही, पैरामीटर के तौर पर ऐसेट का पाथ देना होगा. अपने कोड के आखिर में यह तरीका जोड़ें:

lib/src/menu.dart

Future<void> _onLoadFlutterAssetExample(
    WebViewController controller, BuildContext context) async {
  await controller.loadFlutterAsset('assets/www/index.html');
}

लोकल फ़ाइल लोड करें

अपने डिवाइस पर किसी फ़ाइल को लोड करने के लिए, एक ऐसा तरीका जोड़ा जा सकता है जो loadFile वाला तरीका फिर से इस्तेमाल करेगा. इसके लिए, WebViewController का इस्तेमाल करें. यह String तरीके से फ़ाइल का पाथ ले लेता है.

आपको पहले एक ऐसी फ़ाइल बनानी होगी जिसमें एचटीएमएल कोड हो. इसके लिए, आपको इंपोर्ट के ठीक नीचे मौजूद menu.dart फ़ाइल में, सबसे ऊपर एचटीएमएल कोड को स्ट्रिंग के तौर पर जोड़ना होगा.

lib/src/menu.dart

import 'dart:io';                                   // Add this line,
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';  // And this one.
import 'package:webview_flutter/webview_flutter.dart';

// Add from here ...
const String kExamplePage = '''
<!DOCTYPE html>
<html lang="en">
<head>
<title>Load file or HTML string example</title>
</head>
<body>

<h1>Local demo page</h1>
<p>
 This is an example page used to demonstrate how to load a local file or HTML
 string using the <a href="https://pub.dev/packages/webview_flutter">Flutter
 webview</a> plugin.
</p>

</body>
</html>
''';
// ... to here.

File बनाने और फ़ाइल में एचटीएमएल स्ट्रिंग लिखने के लिए, आपको दो तरीके जोड़ने होंगे. _onLoadLocalFileExample, स्ट्रिंग के तौर पर पाथ देकर फ़ाइल को लोड करेगा. यह पाथ _prepareLocalFile() तरीके से दिखेगा. अपने कोड में ये तरीके जोड़ें:

lib/src/menu.dart

Future<void> _onLoadLocalFileExample(
    WebViewController controller, BuildContext context) async {
  final String pathToIndex = await _prepareLocalFile();

  await controller.loadFile(pathToIndex);
}

static Future<String> _prepareLocalFile() async {
  final String tmpDir = (await getTemporaryDirectory()).path;
  final File indexFile = File('$tmpDir/www/index.html');

  await Directory('$tmpDir/www').create(recursive: true);
  await indexFile.writeAsString(kExamplePage);

  return indexFile.path;
}

एचटीएमएल स्ट्रिंग लोड करें

एचटीएमएल स्ट्रिंग देकर पेज को दिखाना काफ़ी आसान है. WebViewController में loadHtmlString नाम का एक तरीका इस्तेमाल किया जा सकता है, जहां एचटीएमएल स्ट्रिंग को तर्क के तौर पर दिया जा सकता है. इसके बाद, WebView, दिया गया एचटीएमएल पेज दिखाएगा. अपने कोड में यह तरीका जोड़ें:

lib/src/menu.dart

Future<void> _onLoadFlutterAssetExample(
    WebViewController controller, BuildContext context) async {
  await controller.loadFlutterAsset('assets/www/index.html');
}

Future<void> _onLoadLocalFileExample(
    WebViewController controller, BuildContext context) async {
  final String pathToIndex = await _prepareLocalFile();

  await controller.loadFile(pathToIndex);
}

static Future<String> _prepareLocalFile() async {
  final String tmpDir = (await getTemporaryDirectory()).path;
  final File indexFile = File('$tmpDir/www/index.html');

  await Directory('$tmpDir/www').create(recursive: true);
  await indexFile.writeAsString(kExamplePage);

  return indexFile.path;
}

// Add here ...
Future<void> _onLoadHtmlStringExample(
    WebViewController controller, BuildContext context) async {
  await controller.loadHtmlString(kExamplePage);
}
// ... to here.

मेन्यू आइटम जोड़ना

अब ऐसेट सेट हो गई हैं और इस्तेमाल के लिए तैयार हैं. साथ ही, सभी फ़ंक्शन के लिए तरीके बना लिए गए हैं, इसलिए मेन्यू को अपडेट किया जा सकता है. _MenuOptions enum में ये एंट्री जोड़ें:

lib/src/menu.dart

enum _MenuOptions {
  navigationDelegate,
  userAgent,
  javascriptChannel,
  listCookies,
  clearCookies,
  addCookie,
  setCookie,
  removeCookie,
  // Add from here ...
  loadFlutterAsset,
  loadLocalFile,
  loadHtmlString,
  // ... to here.
}

अब जब ईनम अपडेट हो गया है, तो मेन्यू के विकल्प जोड़े जा सकते हैं. साथ ही, उन्हें हाल ही में जोड़े गए हेल्पर तरीकों के साथ जोड़ा जा सकता है. _MenuState क्लास को इस तरह अपडेट करें:

lib/src/menu.dart

class _MenuState extends State<Menu> {
  final cookieManager = WebViewCookieManager();

  @override
  Widget build(BuildContext context) {
    return PopupMenuButton<_MenuOptions>(
      onSelected: (value) async {
        switch (value) {
          case _MenuOptions.navigationDelegate:
            await widget.controller
                .loadRequest(Uri.parse('https://youtube.com'));
          case _MenuOptions.userAgent:
            final userAgent = await widget.controller
                .runJavaScriptReturningResult('navigator.userAgent');
            if (!context.mounted) return;
            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
              content: Text('$userAgent'),
            ));
          case _MenuOptions.javascriptChannel:
            await widget.controller.runJavaScript('''
var req = new XMLHttpRequest();
req.open('GET', "https://api.ipify.org/?format=json");
req.onload = function() {
  if (req.status == 200) {
    let response = JSON.parse(req.responseText);
    SnackBar.postMessage("IP Address: " + response.ip);
  } else {
    SnackBar.postMessage("Error: " + req.status);
  }
}
req.send();''');
          case _MenuOptions.clearCookies:
            await _onClearCookies();
          case _MenuOptions.listCookies:
            await _onListCookies(widget.controller);
          case _MenuOptions.addCookie:
            await _onAddCookie(widget.controller);
          case _MenuOptions.setCookie:
            await _onSetCookie(widget.controller);
          case _MenuOptions.removeCookie:
            await _onRemoveCookie(widget.controller);
          case _MenuOptions.loadFlutterAsset:             // Add from here
            if (!mounted) return;
            await _onLoadFlutterAssetExample(widget.controller, context);
          case _MenuOptions.loadLocalFile:
            if (!mounted) return;
            await _onLoadLocalFileExample(widget.controller, context);
          case _MenuOptions.loadHtmlString:
            if (!mounted) return;
            await _onLoadHtmlStringExample(widget.controller, context);
                                                          // To here.
        }
      },
      itemBuilder: (context) => [
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.navigationDelegate,
          child: Text('Navigate to YouTube'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.userAgent,
          child: Text('Show user-agent'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.javascriptChannel,
          child: Text('Lookup IP Address'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.clearCookies,
          child: Text('Clear cookies'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.listCookies,
          child: Text('List cookies'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.addCookie,
          child: Text('Add cookie'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.setCookie,
          child: Text('Set cookie'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.removeCookie,
          child: Text('Remove cookie'),
        ),
        const PopupMenuItem<_MenuOptions>(                // Add from here
          value: _MenuOptions.loadFlutterAsset,
          child: Text('Load Flutter Asset'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.loadHtmlString,
          child: Text('Load HTML string'),
        ),
        const PopupMenuItem<_MenuOptions>(
          value: _MenuOptions.loadLocalFile,
          child: Text('Load local file'),
        ),                                                // To here.
      ],
    );
  }

ऐसेट, फ़ाइल, और एचटीएमएल स्ट्रिंग की जांच करना

यह जांचने के लिए कि आपने अभी-अभी जो कोड लागू किया है वह काम करता है या नहीं, अपने डिवाइस पर कोड चलाएं और जोड़े गए किसी नए मेन्यू आइटम पर क्लिक करें. ध्यान दें कि _onLoadFlutterAssetExample, एचटीएमएल फ़ाइल के हेडर को नीले रंग में बदलने के लिए, जोड़े गए style.css का इस्तेमाल कैसे करता है.

Android Emulator का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है, जिस पर &#39;लोकल डेमो पेज&#39; लेबल वाला पेज दिख रहा है जिसका टाइटल नीले रंग में है

Android Emulator का स्क्रीन शॉट, जिसमें Flutter ऐप्लिकेशन इस्तेमाल किया जा रहा है. साथ ही, एम्बेड किया गया वेबव्यू दिखाया गया है, जिस पर &#39;लोकल डेमो पेज&#39; लेबल वाला पेज दिख रहा है जिसका टाइटल काले रंग में हो

13. सब हो गया!

बधाई हो!!! आपने कोडलैब पूरा कर लिया है. इस कोडलैब के लिए पूरा कोड पाने के लिए, कोडलैब का डेटा स्टोर करने की जगह पर जाएं.

ज़्यादा जानने के लिए, अन्य Flutter कोड लैब आज़माएं.