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

১. ভূমিকা

সর্বশেষ হালনাগাদ: ২০২১-১০-১৯

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

আপনি যা তৈরি করবেন

এই কোডল্যাবে, আপনি ফ্লাটার এসডিকে ব্যবহার করে একটি ওয়েবভিউ সমন্বিত একটি মোবাইল অ্যাপ ধাপে ধাপে তৈরি করবেন। আপনার অ্যাপটি যা করবে:

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

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

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

আপনি যা শিখবেন

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

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

আপনার যা যা লাগবে

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

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

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

৩. শুরু করা

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

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

$ 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 প্লাগইনটি যোগ করবেন। টার্মিনালে নিম্নলিখিত কমান্ডগুলো চালান।

$ 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 ফাইলটি পরীক্ষা করেন, তাহলে দেখতে পাবেন যে এর dependencies সেকশনে webview_flutter প্লাগইনটির জন্য একটি লাইন রয়েছে।

অ্যান্ড্রয়েড minSDK কনফিগার করুন

অ্যান্ড্রয়েডে 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
    }

৪. ফ্লাটার অ্যাপে WebView উইজেট যোগ করা

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

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

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

$ flutter run

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

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

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

৫. পৃষ্ঠা লোড হওয়ার ইভেন্ট শোনা

WebView উইজেটটি বেশ কয়েকটি পেজ লোড প্রোগ্রেস ইভেন্ট প্রদান করে, যেগুলো আপনার অ্যাপ শুনতে পারে। WebView পেজ লোড চক্র চলাকালীন তিনটি ভিন্ন পেজ লোড ইভেন্ট সক্রিয় হয়: onPageStarted , onProgress , এবং onPageFinished । এই ধাপে আপনি একটি পেজ লোড ইন্ডিকেটর তৈরি করবেন। বাড়তি সুবিধা হিসেবে, এটি দেখাবে যে আপনি WebView কন্টেন্ট এরিয়ার উপরে ফ্লাটার কন্টেন্ট রেন্ডার করতে পারেন।

আপনার অ্যাপে পেজ লোড ইভেন্ট যোগ করা

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 এর মধ্যে রেখেছে এবং পেজ লোড পার্সেন্টেজ ১০০%-এর কম হলে শর্তসাপেক্ষে 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 কন্টেন্ট এরিয়ার উপরে একটি পেজ লোডিং ইন্ডিকেটর দেখতে পাবেন।

৬. WebViewController নিয়ে কাজ করা

WebView উইজেট থেকে WebViewController অ্যাক্সেস করা

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 তৈরি করব। এই অ্যাপে 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 হোমপেজটি দেখানো হচ্ছে।

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

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

WebView আপনার অ্যাপকে একটি NavigationDelegate, যা আপনার অ্যাপকে WebView উইজেটের পেজ নেভিগেশন ট্র্যাক ও নিয়ন্ত্রণ করতে সক্ষম করে। যখন WebView, যেমন যখন কোনো ব্যবহারকারী একটি লিঙ্কে ক্লিক করেন, তখন NavigationDelegate কল করা হয়। WebView নেভিগেশনটি চালিয়ে যাবে কি না, তা নিয়ন্ত্রণ করতে NavigationDelegate কলব্যাকটি ব্যবহার করা যেতে পারে।

একটি কাস্টম নেভিগেশন ডেলিগেট নিবন্ধন করুন

এই ধাপে, আপনি YouTube.com- এ নেভিগেশন ব্লক করার জন্য একটি NavigationDelegate কলব্যাক রেজিস্টার করবেন। উল্লেখ্য, এই সরলীকৃত বাস্তবায়নটি ইনলাইন ইউটিউব কন্টেন্টও ব্লক করে, যা বিভিন্ন ফ্লাটার এপিআই ডকুমেন্টেশন পেজে দেখা যায়।

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

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

পরবর্তী কয়েকটি ধাপে, আপনি 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 আসবে, যা আপনাকে জানাবে যে নেভিগেশন কন্ট্রোলারটি ইউটিউবে যাওয়া ব্লক করে দিয়েছে।

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

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

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

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

জাভাস্ক্রিপ্ট সক্রিয় করতে, আপনাকে 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 জাভাস্ক্রিপ্ট এক্সিকিউট করতে পারে, আপনি মেনুতে runJavaScriptReturningResult মেথডটি ব্যবহার করার জন্য একটি অপশন যোগ করতে পারেন।

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

আপনি যখন 'Show user-agent' মেনু অপশনটিতে ট্যাপ করেন, তখন navigator.userAgent জাভাস্ক্রিপ্ট এক্সপ্রেশনটি এক্সিকিউট করার ফলাফল একটি Snackbar দেখানো হয়। অ্যাপটি রান করার সময়, আপনি হয়তো লক্ষ্য করবেন যে `Flutter.dev` পেজটি দেখতে ভিন্ন লাগছে। জাভাস্ক্রিপ্ট এনাবল করে রান করার ফলেই এমনটা হয়।

আইফোন সিমুলেটরে একটি ফ্লাটার অ্যাপ চলছে, যেটিতে একটি এমবেডেড ওয়েবভিউ রয়েছে। ওয়েবভিউটিতে Flutter.dev হোমপেজটি দেখানো হচ্ছে এবং এতে 'Navigate to YouTube' বা 'Show user-agent' অপশনসহ মেনু আইটেম রয়েছে।

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

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

এখন যেহেতু WebViewWidget আমাদের জাভাস্ক্রিপ্ট চ্যানেলগুলো সম্পর্কে জানে , আপনি অ্যাপটিকে আরও প্রসারিত করার জন্য একটি উদাহরণ যোগ করবেন। এটি করার জন্য, Menu ক্লাসে একটি অতিরিক্ত PopupMenuItem যোগ করুন এবং অতিরিক্ত কার্যকারিতাগুলো যুক্ত করুন।

অতিরিক্ত মেনু অপশনটি দিয়ে _MenuOptions আপডেট করুন, এর জন্য javascriptChannel এনুমারেশন ভ্যালুটি যোগ করুন, এবং 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 Channel Example' মেনু অপশনটি নির্বাচন করেন, তখন এই জাভাস্ক্রিপ্টটি কার্যকর হয়।

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 রিকোয়েস্ট পাঠায় এবং ডিভাইসটির আইপি অ্যাড্রেস ফেরত দেয়। এই ফলাফলটি SnackBar JavascriptChannelpostMessage কল করার মাধ্যমে একটি SnackBar এ দেখানো হয়।

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

আপনার অ্যাপ 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 মালিক হতে হয়, এবং স্টেটলেস উইজেটে পরিবর্তনযোগ্য স্টেট থাকা একটি ক্ষতিকর সংমিশ্রণ।

নিম্নলিখিতভাবে ফলাফলস্বরূপ প্রাপ্ত 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),
    ),
  );
}

জাভাস্ক্রিপ্ট ব্যবহার করে কুকি যোগ করা যায়। জাভাস্ক্রিপ্ট ডকুমেন্টে কুকি যোগ করার জন্য ব্যবহৃত এপিআই সম্পর্কে এমডিএন-এ বিস্তারিতভাবে নথিভুক্ত করা আছে।

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

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

এখন শুধু মেনু অপশনগুলো যোগ করতে হবে এবং সেগুলোকে আপনার সদ্য যোগ করা হেল্পার মেথডগুলোর সাথে সংযুক্ত করতে হবে। _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. ‘কুকি সেট করুন’ নির্বাচন করুন। এটি কুকিটিকে সেট করা হয়েছে বলে রিপোর্ট করবে।
  7. প্রথমে ‘List cookies’ নির্বাচন করুন, এবং সবশেষে ‘Remove cookie’ নির্বাচন করুন।

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

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

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

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

১২. WebView-তে Flutter অ্যাসেট, ফাইল এবং HTML স্ট্রিং লোড করুন।

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

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

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

$ 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>

HTML হেডার স্টাইল সেট করার জন্য style.css ফাইলে নিচের কয়েকটি লাইন ব্যবহার করুন:

assets/www/styles/style.css

h1 {
  color: blue;
}

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

ফ্লাটার অ্যাসেট লোড করুন

আপনার তৈরি করা অ্যাসেটটি লোড করার জন্য, আপনাকে শুধু 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 ফাইলের কোডের একেবারে উপরে, imports-এর ঠিক নিচে HTML কোডটিকে একটি String হিসেবে যোগ করুন।

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 স্ট্রিং দিয়ে কোনো পৃষ্ঠা প্রদর্শন করা বেশ সহজ। WebViewControllerloadHtmlString নামে একটি মেথড আছে, যেখানে আপনি আর্গুমেন্ট হিসেবে 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.
      ],
    );
  }

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

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

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

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

১৩. সব হয়ে গেছে!

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

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