আপনার ফ্লটার অ্যাপে WebView যোগ করা হচ্ছে

1. ভূমিকা

শেষ আপডেট: ২০২১-১০-১৯

WebView Flutter প্লাগইনের সাহায্যে আপনি আপনার Android বা iOS Flutter অ্যাপে একটি WebView উইজেট যোগ করতে পারেন। iOS-এ WebView উইজেটটি WKWebView দ্বারা সমর্থিত, অন্যদিকে Android-এ WebView উইজেটটি WebView দ্বারা সমর্থিত। প্লাগইনটি ওয়েব ভিউতে Flutter উইজেটগুলি রেন্ডার করতে পারে। উদাহরণস্বরূপ, ওয়েব ভিউতে একটি ড্রপ-ডাউন মেনু রেন্ডার করা সম্ভব।

তুমি কী তৈরি করবে

এই কোডল্যাবে, আপনি ধাপে ধাপে একটি মোবাইল অ্যাপ তৈরি করবেন যেখানে Flutter SDK ব্যবহার করে একটি WebView থাকবে। আপনার অ্যাপটি:

  • WebView ওয়েব কন্টেন্ট প্রদর্শন করুন
  • WebView স্ট্যাক করা ফ্লাটার উইজেটগুলি প্রদর্শন করুন
  • পৃষ্ঠা লোডের অগ্রগতির ইভেন্টগুলিতে প্রতিক্রিয়া জানান
  • WebViewController এর মাধ্যমে WebView নিয়ন্ত্রণ করুন
  • NavigationDelegate ব্যবহার করে ওয়েবসাইট ব্লক করুন
  • জাভাস্ক্রিপ্ট এক্সপ্রেশন মূল্যায়ন করুন
  • JavascriptChannels সাহায্যে জাভাস্ক্রিপ্ট থেকে কলব্যাক পরিচালনা করুন
  • কুকিজ সেট করুন, সরান, যোগ করুন বা দেখান
  • HTML ধারণকারী সম্পদ, ফাইল বা স্ট্রিং থেকে HTML লোড এবং প্রদর্শন করুন

আইফোন সিমুলেটরটি একটি ফ্লটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যা Flutter.dev হোমপেজ দেখায়।

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যা Flutter.dev হোমপেজ দেখাচ্ছে।

তুমি কি শিখবে

এই কোডল্যাবে আপনি শিখবেন কিভাবে webview_flutter প্লাগইনটি বিভিন্ন উপায়ে ব্যবহার করতে হয়, যার মধ্যে রয়েছে:

  • কিভাবে webview_flutter প্লাগইন কনফিগার করবেন
  • পৃষ্ঠা লোডের অগ্রগতির ইভেন্টগুলি কীভাবে শুনবেন
  • পৃষ্ঠা নেভিগেশন কীভাবে নিয়ন্ত্রণ করবেন
  • WebView তার ইতিহাসের মাধ্যমে পিছনে এবং সামনে যেতে কীভাবে নির্দেশ দেবেন
  • জাভাস্ক্রিপ্ট কীভাবে মূল্যায়ন করবেন, যার মধ্যে ফিরে আসা ফলাফল ব্যবহার করাও অন্তর্ভুক্ত।
  • জাভাস্ক্রিপ্ট থেকে ডার্ট কোডে কল করার জন্য কলব্যাক কীভাবে নিবন্ধন করবেন
  • কুকিজ কীভাবে পরিচালনা করবেন
  • HTML ধারণকারী সম্পদ, ফাইল বা স্ট্রিং থেকে HTML পৃষ্ঠাগুলি কীভাবে লোড এবং প্রদর্শন করবেন

তোমার যা লাগবে

2. আপনার ফ্লাটার ডেভেলপমেন্ট পরিবেশ সেট আপ করুন

এই ল্যাবটি সম্পূর্ণ করার জন্য আপনার দুটি সফটওয়্যারের প্রয়োজন — ফ্লটার এসডিকে এবং একটি এডিটর

আপনি এই ডিভাইসগুলির যেকোনো একটি ব্যবহার করে কোডল্যাব চালাতে পারেন:

  • আপনার কম্পিউটারের সাথে সংযুক্ত একটি বাস্তব Android বা iOS ডিভাইস এবং ডেভেলপার মোডে সেট করা আছে।
  • iOS সিমুলেটর (Xcode টুল ইনস্টল করার প্রয়োজন)।
  • অ্যান্ড্রয়েড এমুলেটর (অ্যান্ড্রয়েড স্টুডিওতে সেটআপ প্রয়োজন)।

৩. শুরু করা

ফ্লাটার দিয়ে শুরু করা

নতুন ফ্লটার প্রজেক্ট তৈরির বিভিন্ন উপায় রয়েছে, যার মধ্যে অ্যান্ড্রয়েড স্টুডিও এবং ভিজ্যুয়াল স্টুডিও কোড উভয়ই এই কাজের জন্য টুলিং প্রদান করে। হয় একটি প্রজেক্ট তৈরি করতে লিঙ্ক করা পদ্ধতিগুলি অনুসরণ করুন, অথবা একটি সহজ কমান্ড লাইন টার্মিনালে নিম্নলিখিত কমান্ডগুলি কার্যকর করুন।

$ 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 প্লাগইন যোগ করা হচ্ছে

একটি Flutter অ্যাপে অতিরিক্ত ক্ষমতা যোগ করা Pub প্যাকেজ ব্যবহার করে সবচেয়ে ভালোভাবে সম্পন্ন করা হয়। এই কোডল্যাবে আপনি আপনার প্রোজেক্টে webview_flutter প্লাগইন যোগ করবেন। টার্মিনালে নিম্নলিখিত কমান্ডগুলি চালান।

$ cd webview_in_flutter
$ flutter pub add webview_flutter
Resolving dependencies...
Downloading packages...
  collection 1.18.0 (1.19.0 available)
  leak_tracker 10.0.5 (10.0.7 available)
  leak_tracker_flutter_testing 3.0.5 (3.0.7 available)
  material_color_utilities 0.11.1 (0.12.0 available)
+ plugin_platform_interface 2.1.8
  string_scanner 1.2.0 (1.3.0 available)
  test_api 0.7.2 (0.7.3 available)
+ webview_flutter 4.9.0
+ webview_flutter_android 3.16.7
+ webview_flutter_platform_interface 2.10.0
+ webview_flutter_wkwebview 3.15.0
Changed 5 dependencies!
6 packages have newer versions incompatible with dependency constraints.
Try `flutter pub outdated` for more information.

আপনি যদি আপনার pubspec.yaml পরীক্ষা করেন, তাহলে আপনি এখন দেখতে পাবেন যে এটিতে webview_flutter প্লাগইনের জন্য নির্ভরতা বিভাগে একটি লাইন রয়েছে।

অ্যান্ড্রয়েড মিনএসডিকে কনফিগার করুন

অ্যান্ড্রয়েডে webview_flutter প্লাগইন ব্যবহার করার জন্য আপনাকে minSDK কে 20 এ সেট করতে হবে। আপনার 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
    }

৪. ফ্লাটার অ্যাপে ওয়েবভিউ উইজেট যোগ করা

এই ধাপে আপনি আপনার অ্যাপ্লিকেশনে একটি WebView যোগ করবেন। WebViews হল বিল্ট-ইন ভিউ হোস্ট করা হয় এবং একজন অ্যাপ ডেভেলপার হিসেবে আপনার অ্যাপে এই বিল্ট-ইন ভিউগুলি কীভাবে হোস্ট করবেন তা বেছে নেওয়ার অধিকার আপনার আছে। Android-এ আপনার কাছে Android-এর জন্য ডিফল্ট ভার্চুয়াল ডিসপ্লে এবং হাইব্রিড কম্পোজিশনের মধ্যে একটি পছন্দ করার সুযোগ রয়েছে। তবে, iOS সর্বদা হাইব্রিড কম্পোজিশন ব্যবহার করে।

ভার্চুয়াল ডিসপ্লে এবং হাইব্রিড কম্পোজিশনের মধ্যে পার্থক্য সম্পর্কে বিস্তারিত আলোচনার জন্য, প্ল্যাটফর্ম ভিউ সহ আপনার ফ্লটার অ্যাপে নেটিভ অ্যান্ড্রয়েড এবং 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 এ এটি চালানোর সময় আপনার ডিভাইসে একটি পূর্ণ-ব্লিড ব্রাউজার উইন্ডো হিসেবে WebView দেখাবে, যার অর্থ হল ব্রাউজারটি আপনার ডিভাইসে পূর্ণ-স্ক্রিনে কোনও ধরণের সীমানা বা মার্জিন ছাড়াই প্রদর্শিত হবে। স্ক্রোল করার সময়, আপনি পৃষ্ঠার কিছু অংশ লক্ষ্য করবেন যা কিছুটা অদ্ভুত দেখাতে পারে। এর কারণ হল JavaScript অক্ষম করা আছে এবং flutter.dev সঠিকভাবে রেন্ডার করার জন্য JavaScript প্রয়োজন।

অ্যাপটি চালানো হচ্ছে

iOS অথবা Android-এ Flutter অ্যাপটি চালান এবং একটি Webview দেখুন, যা flutter.dev ওয়েবসাইট প্রদর্শন করে। বিকল্পভাবে, একটি Android এমুলেটর অথবা একটি iOS সিমুলেটরে অ্যাপটি চালান। প্রাথমিক WebView URLটি প্রতিস্থাপন করতে দ্বিধা করবেন না, উদাহরণস্বরূপ, আপনার নিজস্ব ওয়েবসাইট।

$ flutter run

ধরে নিচ্ছি যে আপনার ডিভাইসে অ্যাপটি কম্পাইল এবং স্থাপন করার পরে, আপনার কাছে উপযুক্ত সিমুলেটর বা এমুলেটর চলছে, অথবা একটি ফিজিক্যাল ডিভাইস সংযুক্ত আছে, তাহলে আপনি নিম্নলিখিতগুলির মতো কিছু দেখতে পাবেন:

আইফোন সিমুলেটরটি একটি ফ্লটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যা Flutter.dev হোমপেজ দেখায়।

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যা Flutter.dev হোমপেজ দেখাচ্ছে।

৫. পৃষ্ঠা লোড ইভেন্টগুলি শোনা

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 কন্টেন্ট এরিয়ার উপরে একটি পৃষ্ঠা লোডিং সূচক দেখতে পাবেন।

৬. ওয়েবভিউকন্ট্রোলারের সাথে কাজ করা

ওয়েবভিউ উইজেট থেকে ওয়েবভিউকন্ট্রোলার অ্যাক্সেস করা

WebView উইজেটটি একটি WebViewController দিয়ে প্রোগ্রাম্যাটিক নিয়ন্ত্রণ সক্ষম করে। WebView উইজেট তৈরির পর কলব্যাকের মাধ্যমে এই কন্ট্রোলারটি উপলব্ধ করা হয়। এই কন্ট্রোলারের অ্যাসিঙ্ক্রোনাস প্রকৃতি এটিকে ডার্টের অ্যাসিঙ্ক্রোনাস 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 নিয়ন্ত্রণ করতে পারে।

অ্যাপবারে নেভিগেশন নিয়ন্ত্রণ যোগ করা হচ্ছে

আপডেটেড WebViewStack এবং নতুন তৈরি NavigationControls হাতে পেয়ে, এখন সময় এসেছে আপনার সবকিছু একসাথে একটি আপডেটেড WebViewApp এ স্থাপন করার। এখানেই আমরা শেয়ার্ড WebViewController তৈরি করব। এই অ্যাপে Widget tree-এর উপরে 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
    );
  }
}

অ্যাপটি চালানোর সময় নিয়ন্ত্রণ সহ একটি ওয়েব পৃষ্ঠা প্রকাশিত হওয়া উচিত:

আইফোন সিমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যা Flutter.dev হোমপেজকে পূর্ববর্তী পৃষ্ঠা, পরবর্তী পৃষ্ঠা এবং পৃষ্ঠা পুনরায় লোড নিয়ন্ত্রণ সহ দেখায়।

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যা Flutter.dev হোমপেজকে পূর্ববর্তী পৃষ্ঠা, পরবর্তী পৃষ্ঠা এবং পৃষ্ঠা পুনরায় লোড নিয়ন্ত্রণ সহ দেখায়।

৭. নেভিগেশনডেলিগেটের সাহায্যে নেভিগেশনের উপর নজর রাখা

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-এ পূর্ণ পৃষ্ঠার নেভিগেশন ব্লক করা যায় এবং API ডকুমেন্টেশনে ইনলাইন YouTube কন্টেন্টের অনুমতি দেওয়া যায়।

৮. অ্যাপবারে একটি মেনু বোতাম যোগ করা

পরবর্তী কয়েকটি ধাপে, আপনি AppBar উইজেটে একটি মেনু বোতাম তৈরি করবেন যা জাভাস্ক্রিপ্ট মূল্যায়ন, জাভাস্ক্রিপ্ট চ্যানেল চালু এবং কুকিজ পরিচালনা করতে ব্যবহৃত হবে। সব মিলিয়ে, এটি সত্যিই একটি কার্যকর মেনু।

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

যখন ব্যবহারকারী Navigate to 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),
    );
  }
}

আপনার অ্যাপটি চালান এবং "Navigate to YouTube" মেনু আইটেমটিতে আলতো চাপুন। আপনাকে একটি SnackBar দিয়ে স্বাগত জানানো হবে যেখানে আপনাকে জানানো হবে যে নেভিগেশন কন্ট্রোলার YouTube-এ নেভিগেট করা বন্ধ করে দিয়েছে।

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যেখানে Flutter.dev হোমপেজ দেখানো হচ্ছে এবং একটি মেনু আইটেম রয়েছে যেখানে 'YouTube-এ নেভিগেট করুন' বিকল্পটি দেখানো হচ্ছে।

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যেখানে Flutter.dev হোমপেজে 'm.youtube.com-এ নেভিগেশন ব্লক করা হচ্ছে' লেখা পপ আপে দেখানো হচ্ছে।

৯. জাভাস্ক্রিপ্ট মূল্যায়ন করা

WebViewController বর্তমান পৃষ্ঠার প্রেক্ষাপটে জাভাস্ক্রিপ্ট এক্সপ্রেশন মূল্যায়ন করতে পারে। জাভাস্ক্রিপ্ট মূল্যায়নের দুটি ভিন্ন উপায় রয়েছে: যে জাভাস্ক্রিপ্ট কোড কোনও মান ফেরত দেয় না, তার জন্য runJavaScript ব্যবহার করুন, এবং যে জাভাস্ক্রিপ্ট কোড কোনও মান ফেরত দেয়, তার জন্য runJavaScriptReturningResult ব্যবহার করুন।

জাভাস্ক্রিপ্ট সক্রিয় করতে, আপনাকে javaScriptMode প্রোপার্টিটি JavascriptMode.unrestricted এ সেট করে WebViewController কনফিগার করতে হবে। ডিফল্টরূপে, javascriptMode JavascriptMode.disabled এ সেট করা থাকে।

_WebViewStackState ক্লাসটি আপডেট করার জন্য javascriptMode সেটিংটি নিম্নরূপ যোগ করুন:

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 জাভাস্ক্রিপ্ট চালাতে পারে, আপনি runJavaScriptReturningResult পদ্ধতি ব্যবহার করার জন্য মেনুতে একটি বিকল্প যোগ করতে পারেন।

আপনার এডিটর অথবা কোন কিবোর্ডের কাজ ব্যবহার করে, মেনু ক্লাসটিকে 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.
      ],
    );
  }
}

'ব্যবহারকারী-এজেন্ট দেখান' মেনু অপশনে ট্যাপ করলে, জাভাস্ক্রিপ্ট এক্সপ্রেশন navigator.userAgent কার্যকর করার ফলাফল একটি Snackbar এ প্রদর্শিত হবে। অ্যাপটি চালানোর সময়, আপনি লক্ষ্য করতে পারেন যে Flutter.dev পৃষ্ঠাটি ভিন্ন দেখাচ্ছে। এটি জাভাস্ক্রিপ্ট সক্ষম করে চালানোর ফলাফল।

আইফোন সিমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যেখানে Flutter.dev হোমপেজ দেখানো হচ্ছে এবং একটি মেনু আইটেম রয়েছে যেখানে 'YouTube-এ নেভিগেট করুন' অথবা 'ব্যবহারকারী-এজেন্ট দেখান' বিকল্পগুলি দেখানো হচ্ছে।

আইফোন সিমুলেটরটি একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যেখানে Flutter.dev হোমপেজ দেখাচ্ছে এবং টোস্ট পপ আপে ব্যবহারকারী এজেন্ট স্ট্রিং দেখাচ্ছে।

১০. জাভাস্ক্রিপ্ট চ্যানেলের সাথে কাজ করা

জাভাস্ক্রিপ্ট চ্যানেলগুলি আপনার অ্যাপটিকে WebViewWidget এর জাভাস্ক্রিপ্ট প্রসঙ্গে কলব্যাক হ্যান্ডলার নিবন্ধন করতে সক্ষম করে যা অ্যাপের ডার্ট কোডে মানগুলি ফিরিয়ে আনতে ব্যবহার করা যেতে পারে। এই ধাপে আপনি একটি 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 Channel-এর জন্য, JavaScript প্রসঙ্গে একটি চ্যানেল অবজেক্ট JavaScript Channel name একই নামের একটি উইন্ডো প্রপার্টি হিসেবে উপলব্ধ করা হয়। JavaScript প্রেক্ষাপট থেকে এটি ব্যবহার করার জন্য JavaScript Channel-এ postMessage কল করে একটি বার্তা পাঠানো হয় যা JavascriptChannel এর onMessageReceived কলব্যাক হ্যান্ডলারে পাঠানো হয়।

আপনার পূর্বে যোগ করা জাভাস্ক্রিপ্ট চ্যানেলটি ব্যবহার করতে, আরেকটি মেনু আইটেম যোগ করুন যা জাভাস্ক্রিপ্ট প্রসঙ্গে একটি XMLHttpRequest কার্যকর করে এবং SnackBar জাভাস্ক্রিপ্ট চ্যানেল ব্যবহার করে ফলাফলগুলি ফেরত পাঠায়।

এখন যেহেতু WebViewWidget আমাদের JavaScript Channels সম্পর্কে জানে , আপনি অ্যাপটিকে আরও প্রসারিত করার জন্য একটি উদাহরণ যোগ করবেন। এটি করার জন্য, 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.
      ],
    );
  }
}

ব্যবহারকারী যখন জাভাস্ক্রিপ্ট চ্যানেল উদাহরণ মেনু বিকল্পটি বেছে নেয় তখন এই জাভাস্ক্রিপ্টটি কার্যকর করা হয়।

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();

এই কোডটি একটি পাবলিক আইপি অ্যাড্রেস এপিআই-তে একটি GET অনুরোধ পাঠায়, যা ডিভাইসের আইপি অ্যাড্রেস ফেরত দেয়। এই ফলাফলটি SnackBarSnackBar JavascriptChannelpostMessage ইনভোক করে দেখানো হয়।

১১. কুকিজ পরিচালনা করা

আপনার অ্যাপটি CookieManager ক্লাস ব্যবহার করে WebView এ কুকি পরিচালনা করতে পারে। এই ধাপে, আপনি কুকির একটি তালিকা দেখাবেন, কুকির তালিকা সাফ করবেন, কুকি মুছে ফেলবেন এবং নতুন কুকি সেট করবেন। প্রতিটি কুকি ব্যবহারের ক্ষেত্রে _MenuOptions এ এন্ট্রি যোগ করুন নিম্নরূপ:

lib/src/menu.dart

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

এই ধাপের বাকি পরিবর্তনগুলি Menu ক্লাসের উপর দৃষ্টি নিবদ্ধ করে, যার মধ্যে Menu ক্লাসকে stateless থেকে stateful এ রূপান্তর করা অন্তর্ভুক্ত। এই পরিবর্তনটি গুরুত্বপূর্ণ কারণ Menu এর CookieManager এর মালিকানা থাকা প্রয়োজন, এবং stateless উইজেটগুলিতে mutable state একটি খারাপ সমন্বয়।

ফলাফলস্বরূপ State ক্লাসে 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 এ সহায়ক ফাংশন যোগ করবেন যা, এখনও যোগ করা হয়নি এমন মেনু আইটেমগুলি দ্বারা আহ্বান করা হবে।

সকল কুকিজের একটি তালিকা পান

আপনি জাভাস্ক্রিপ্ট ব্যবহার করে সকল কুকির তালিকা পাবেন। এটি অর্জনের জন্য, _MenuState ক্লাসের শেষে _onListCookies নামে একটি হেল্পার পদ্ধতি যোগ করুন। runJavaScriptReturningResult পদ্ধতি ব্যবহার করে, আপনার হেল্পার পদ্ধতিটি জাভাস্ক্রিপ্ট প্রসঙ্গে 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.'),
    ),
  );
}

সমস্ত কুকি সাফ করুন

WebView-এর সমস্ত কুকি মুছে ফেলার জন্য, 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),
    ),
  );
}

জাভাস্ক্রিপ্ট ব্যবহার করে কুকি যোগ করা যেতে পারে। জাভাস্ক্রিপ্ট ডকুমেন্টে কুকি যোগ করার জন্য ব্যবহৃত API 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.'),
    ),
  );
}

কুকি ম্যানেজার ব্যবহার করেও নিম্নরূপে কুকি সেট করা যেতে পারে।

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

কুকি ম্যানেজার মেনু আইটেম যোগ করা হচ্ছে

এখন যা বাকি আছে তা হল মেনু অপশনগুলো যোগ করা এবং সেগুলোকে আপনার যোগ করা হেল্পার মেথডের সাথে সংযুক্ত করা। _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.
      ],
    );
  }

কুকি ম্যানেজার ব্যবহার করা

অ্যাপটিতে আপনার যোগ করা সমস্ত কার্যকারিতা ব্যবহার করতে, নিম্নলিখিত পদক্ষেপগুলি চেষ্টা করুন:

  1. "কুকি তালিকা" নির্বাচন করুন। এটি flutter.dev দ্বারা সেট করা Google Analytics কুকিগুলির তালিকা তৈরি করবে।
  2. "কুকিজ সাফ করুন" নির্বাচন করুন। এটি রিপোর্ট করবে যে কুকিগুলি সত্যিই সাফ করা হয়েছে।
  3. আবার "কুকিজ সাফ করুন" নির্বাচন করুন। এটি রিপোর্ট করবে যে কোনও কুকি সাফ করার জন্য উপলব্ধ ছিল না।
  4. "কুকি তালিকা" নির্বাচন করুন। এটি রিপোর্ট করবে যে কোনও কুকি নেই।
  5. "কুকি যোগ করুন" নির্বাচন করুন। এটি কুকি যোগ করা হয়েছে বলে রিপোর্ট করবে।
  6. Set cookie নির্বাচন করুন। এটি কুকিটিকে সেট হিসাবে রিপোর্ট করবে।
  7. তালিকা কুকিজ নির্বাচন করুন, এবং তারপর চূড়ান্ত flourish হিসাবে, কুকি অপসারণ নির্বাচন করুন।

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যেখানে Flutter.dev হোমপেজ দেখানো হয়েছে যেখানে মেনু বিকল্পগুলির একটি তালিকা রয়েছে যেখানে YouTube-এ নেভিগেট করা, ব্যবহারকারী এজেন্ট দেখানো এবং ব্রাউজারের কুকি জারের সাথে ইন্টারঅ্যাক্ট করা অন্তর্ভুক্ত রয়েছে।

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যেখানে Flutter.dev হোমপেজ দেখাচ্ছে এবং একটি টোস্ট ডায়ালগ রয়েছে যেখানে ব্রাউজারে সেট করা কুকিজ দেখাচ্ছে।

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যেখানে Flutter.dev হোমপেজ দেখাচ্ছে যেখানে একটি টোস্ট ডায়ালগ রয়েছে যেখানে লেখা আছে 'কুকিজ ছিল। এখন সেগুলো চলে গেছে!'

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যেখানে Flutter.dev হোমপেজটি দেখানো হচ্ছে এবং একটি টোস্ট ডায়ালগ রয়েছে যেখানে লেখা আছে 'কাস্টম কুকি যোগ করা হয়েছে।'

১২. ওয়েবভিউতে ফ্লটার অ্যাসেট, ফাইল এবং HTML স্ট্রিং লোড করুন

আপনার অ্যাপ বিভিন্ন পদ্ধতি ব্যবহার করে HTML ফাইল লোড করতে পারে এবং WebView-এ প্রদর্শন করতে পারে। এই ধাপে আপনি pubspec.yaml ফাইলে নির্দিষ্ট একটি Flutter সম্পদ লোড করবেন, নির্দিষ্ট পথে অবস্থিত একটি ফাইল লোড করবেন এবং একটি HTML স্ট্রিং ব্যবহার করে একটি পৃষ্ঠা লোড করবেন।

যদি আপনি একটি নির্দিষ্ট পাথে অবস্থিত একটি ফাইল লোড করতে চান, তাহলে আপনাকে pubspec.yamlpath_provider যোগ করতে হবে। এটি ফাইল সিস্টেমে সাধারণত ব্যবহৃত অবস্থানগুলি খুঁজে বের করার জন্য একটি Flutter প্লাগইন।

কমান্ড লাইনে, নিম্নলিখিত কমান্ডটি চালান:

$ flutter pub add path_provider

সম্পদ লোড করার জন্য আমাদের 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 ফাইলে কপি করে পেস্ট করুন:

সম্পদ/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 এর জন্য HTML হেডার স্টাইল সেট করতে নিম্নলিখিত কয়েকটি লাইন ব্যবহার করুন:

সম্পদ/www/styles/style.css

h1 {
  color: blue;
}

এখন যেহেতু সম্পদগুলি সেট করা হয়েছে এবং ব্যবহারের জন্য প্রস্তুত, আপনি Flutter সম্পদ, ফাইল বা HTML স্ট্রিং লোড এবং প্রদর্শনের জন্য প্রয়োজনীয় পদ্ধতিগুলি বাস্তবায়ন করতে পারেন।

ফ্লাটার সম্পদ লোড করুন

আপনার তৈরি করা সম্পদটি লোড করার জন্য, আপনাকে কেবল WebViewController ব্যবহার করে loadFlutterAsset পদ্ধতিটি কল করতে হবে এবং প্যারামিটার হিসাবে সম্পদের পথটি দিতে হবে। আপনার কোডের শেষে নিম্নলিখিত পদ্ধতিটি যোগ করুন:

lib/src/menu.dart

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

স্থানীয় ফাইল লোড করুন

আপনার ডিভাইসে একটি ফাইল লোড করার জন্য আপনি এমন একটি পদ্ধতি যোগ করতে পারেন যা loadFile পদ্ধতি ব্যবহার করবে, আবার WebViewController ব্যবহার করে যা ফাইলের পাথ ধারণকারী একটি String নেয়।

প্রথমে আপনাকে HTML কোড সম্বলিত একটি ফাইল তৈরি করতে হবে। আপনি আমদানির ঠিক নীচে menu.dart ফাইলে আপনার কোডের উপরে একটি স্ট্রিং হিসেবে HTML কোড যুক্ত করে এটি করতে পারেন।

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 তৈরি করতে এবং ফাইলটিতে HTML স্ট্রিং লিখতে আপনাকে দুটি পদ্ধতি যোগ করতে হবে। _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;
}

HTML স্ট্রিং লোড করুন

HTML স্ট্রিং প্রদান করে একটি পৃষ্ঠা প্রদর্শন করা বেশ সহজ। WebViewController এর একটি পদ্ধতি আছে যা আপনি ব্যবহার করতে পারেন যার নাম loadHtmlString যেখানে আপনি HTML স্ট্রিংকে একটি আর্গুমেন্ট হিসেবে দিতে পারেন। WebView এরপর প্রদত্ত HTML পৃষ্ঠাটি প্রদর্শন করবে। আপনার কোডে নিম্নলিখিত পদ্ধতিটি যোগ করুন:

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.
}

এখন যেহেতু enum আপডেট করা হয়েছে, আপনি মেনু বিকল্পগুলি যোগ করতে পারেন এবং সেগুলিকে আপনার যোগ করা সহায়ক পদ্ধতিগুলির সাথে সংযুক্ত করতে পারেন। _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.
      ],
    );
  }

সম্পদ, ফাইল এবং HTML স্ট্রিং পরীক্ষা করা হচ্ছে

আপনার প্রয়োগ করা কোডটি কাজ করেছে কিনা তা পরীক্ষা করার জন্য, আপনি আপনার ডিভাইসে কোডটি চালাতে পারেন এবং নতুন যোগ করা মেনু আইটেমগুলির একটিতে ক্লিক করতে পারেন। লক্ষ্য করুন কিভাবে _onLoadFlutterAssetExample আমাদের যোগ করা style.css ব্যবহার করে HTML ফাইলের হেডারটি নীল রঙে পরিবর্তন করে।

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যার শিরোনাম নীল রঙে 'স্থানীয় ডেমো পৃষ্ঠা' লেবেলযুক্ত একটি পৃষ্ঠা দেখানো হচ্ছে।

অ্যান্ড্রয়েড এমুলেটর একটি ফ্লাটার অ্যাপ চালাচ্ছে যেখানে একটি এমবেডেড ওয়েবভিউ রয়েছে যার শিরোনাম কালো রঙের 'স্থানীয় ডেমো পৃষ্ঠা' লেবেলযুক্ত একটি পৃষ্ঠা দেখাচ্ছে।

১৩. সব শেষ!

অভিনন্দন!!! তুমি কোডল্যাবটি সম্পন্ন করেছ। এই কোডল্যাবের জন্য সম্পূর্ণ কোডটি কোডল্যাব রিপোজিটরিতে খুঁজে পেতে পারো।

আরও জানতে, অন্যান্য Flutter কোডল্যাবগুলি চেষ্টা করে দেখুন।