Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webview_flutter canGoBack problem in Separate Window #148536

Closed
YashExpert10 opened this issue May 17, 2024 · 6 comments
Closed

Webview_flutter canGoBack problem in Separate Window #148536

YashExpert10 opened this issue May 17, 2024 · 6 comments
Labels
r: invalid Issue is closed as not valid

Comments

@YashExpert10
Copy link

YashExpert10 commented May 17, 2024

Steps to reproduce

Two different windows are open in the bottom navigation bar but on pressing the back button both Webview 1 and Webview 2 webpages are going back, whereas only one should be back.
When I do window.history.back(), I get the same result by using runJavaScript.
Controller should be different for different window.

Sample Code.zip

Expected results

Result should be only one window is back when press back button.
Controllers and runJavaScript and other method should not be conflict for different Class.

Actual results

webview.back.problem.mp4

Code sample

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart';
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';

void main() {
  // runApp(MyApp());
  runApp(const MaterialApp(home: TabsView()));
}

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

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

class _TabsViewState extends State<TabsView> {
  int _currentIndex = 0;
  int tabIndex = 0;

  void onTabTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(

      body: Stack(
        children: [
          Offstage(
            offstage: _currentIndex != 0,
            child: const First(),
          ),
          Offstage(
            offstage: _currentIndex != 1,
            child: const Second(),
          ),
          Offstage(
            offstage: _currentIndex != 2,
            child: ResetView(),
          ),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        selectedItemColor: Colors.deepOrange[800],
        backgroundColor: Colors.grey[300],
        unselectedItemColor: Colors.grey[800],
        currentIndex: _currentIndex,
        onTap: onTabTapped,
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Webview 1',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.web),
            label: 'Webview 2',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.restore),
            label: 'Restore',
          ),
        ],
      ),
    );
  }
}

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

  @override
  State<First> createState() => _FirstState();
}

class _FirstState extends State<First> {
  late final WebViewController _controller1;

  @override
  void initState() {
    super.initState();

    late final PlatformWebViewControllerCreationParams params;
    if (WebViewPlatform.instance is WebKitWebViewPlatform) {
      params = WebKitWebViewControllerCreationParams(
        allowsInlineMediaPlayback: true,
        mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
      );
    } else {
      params = const PlatformWebViewControllerCreationParams();
    }

    final WebViewController controller =
        WebViewController.fromPlatformCreationParams(params);

    controller
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setBackgroundColor(const Color(0x00000000))
      ..setNavigationDelegate(
        NavigationDelegate(onProgress: (int progress) {
          debugPrint('WebView is loading (progress : $progress%)');
        }, onPageStarted: (String url) {
          debugPrint('Page started loading: $url');
        }, onPageFinished: (String url) {
          debugPrint('Page finished loading: $url');
        }, onWebResourceError: (WebResourceError error) {
          debugPrint('Page resource error: $error');
        }, onNavigationRequest: (NavigationRequest request) {
          if (request.url.startsWith('https://www.youtube.com/')) {
            debugPrint('blocking navigation to ${request.url}');
            return NavigationDecision.prevent;
          }
          debugPrint('allowing navigation to ${request.url}');
          return NavigationDecision.navigate;
        }, onUrlChange: (UrlChange change) {
          debugPrint('url change to ${change.url}');
        }),
      )
      ..loadRequest(Uri.parse('https://stackoverflow.com'));

    if (controller.platform is AndroidWebViewController) {
      AndroidWebViewController.enableDebugging(true);
      (controller.platform as AndroidWebViewController)
          .setMediaPlaybackRequiresUserGesture(false);
    }
    // #enddocregion platform_features

    _controller1 = controller;
  }

  Future<bool> _goBackCall1(BuildContext context) async {
    if (await _controller1.canGoBack()) {
      _controller1.goBack();
      return Future.value(false);
    } else {
      // SystemNavigator.pop();
      return Future.value(true);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Flutter WebView1"),
      ),
      body: PopScope(
        canPop: false, 
        onPopInvoked: (bool didPop) {
          if (didPop) {
            print('didPop call: $didPop');
            return;
          }
          _goBackCall1(context);
        },
        child: SafeArea(
          child: Stack(
            children: [
              WebViewWidget(controller: _controller1),
            ],
          ),
        ),
      ),
    );
  }
}

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

  @override
  State<Second> createState() => _SecondState();
}

class _SecondState extends State<Second> {
  late final WebViewController _controller2;

  @override
  void initState() {
    super.initState();

    late final PlatformWebViewControllerCreationParams params;
    if (WebViewPlatform.instance is WebKitWebViewPlatform) {
      params = WebKitWebViewControllerCreationParams(
        allowsInlineMediaPlayback: true,
        mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
      );
    } else {
      params = const PlatformWebViewControllerCreationParams();
    }

    final WebViewController controller =
        WebViewController.fromPlatformCreationParams(params);

    controller
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setBackgroundColor(const Color(0x00000000))
      ..setNavigationDelegate(
        NavigationDelegate(onProgress: (int progress) {
          debugPrint('WebView is loading (progress : $progress%)');
        }, onPageStarted: (String url) {
          debugPrint('Page started loading: $url');
        }, onPageFinished: (String url) {
          debugPrint('Page finished loading: $url');
        }, onWebResourceError: (WebResourceError error) {
          debugPrint('Page resource error: $error');
        }, onNavigationRequest: (NavigationRequest request) {
          if (request.url.startsWith('https://www.youtube.com/')) {
            debugPrint('blocking navigation to ${request.url}');
            return NavigationDecision.prevent;
          }
          debugPrint('allowing navigation to ${request.url}');
          return NavigationDecision.navigate;
        }, onUrlChange: (UrlChange change) {
          debugPrint('url change to ${change.url}');
        }),
      )
      ..loadRequest(Uri.parse('https://google.com'));

    if (controller.platform is AndroidWebViewController) {
      AndroidWebViewController.enableDebugging(true);
      (controller.platform as AndroidWebViewController)
          .setMediaPlaybackRequiresUserGesture(false);
    }

    _controller2 = controller;
  }

  Future<bool> _goBackCall2(BuildContext context) async {
    if (await _controller2.canGoBack()) {
      _controller2.goBack();
      return Future.value(false);
    } else {
      return Future.value(true);
    }
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Flutter WebView2"),
      ),
      // body: WebViewWidget(controller: youtubeVideoWebViewController),
      body: PopScope(
        canPop: false, //webview me false hi sahi h
        onPopInvoked: (bool didPop) {
          if (didPop) {
            print('didPop call: $didPop');
            return;
          }
          _goBackCall2(context);
        },
        child: SafeArea(
          child: Stack(
            children: [
              WebViewWidget(controller: _controller2),
            ],
          ),
        ),
      ),
    );
  }
}

class ResetView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ResetView'),
      ),
      body: Container(
        padding: const EdgeInsets.all(10.0),
        child: const Column(
          children: [
            Text('Later Use...'),
          ],
        ),
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
[Paste your logs here]

Flutter Doctor output

PS D:\All_Projects\start_web> flutter doctor -v
[√] Flutter (Channel stable, 3.19.0, on Microsoft Windows [Version 10.0.19045.1889], locale en-IN)
• Flutter version 3.19.0 on channel stable at C:\Src\flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision bae5e49 (3 months ago), 2024-02-13 17:46:18 -0800
• Engine revision 04817c99c9
• Dart version 3.3.0
• DevTools version 2.31.1

[√] Windows Version (Installed version of Windows is version 10 or higher)

[√] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
• Android SDK at C:\Users\Ashish\AppData\Local\Android\sdk
• Platform android-34, build-tools 33.0.0
• Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
• Java version OpenJDK Runtime Environment (build 11.0.12+7-b1504.28-7817840)
• All Android licenses accepted.

[√] Chrome - develop for the web
• Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[X] Visual Studio - develop Windows apps
X Visual Studio not installed; this is necessary to develop Windows apps.
Download at https://visualstudio.microsoft.com/downloads/.
Please install the "Desktop development with C++" workload, including all of its default components

[√] Android Studio (version 2021.2)
• Android Studio at C:\Program Files\Android\Android Studio
• Flutter plugin can be installed from:
https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 11.0.12+7-b1504.28-7817840)

[√] VS Code (version 1.89.1)
• VS Code at C:\Users\Ashish\AppData\Local\Programs\Microsoft VS Code
• Flutter extension version 3.88.0

[√] Connected device (4 available)
• RMP2106 (mobile) • 12E230831006641 • android-arm64 • Android 11 (API 30)
• Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.19045.1889]
• Chrome (web) • chrome • web-javascript • Google Chrome 124.0.6367.207
• Edge (web) • edge • web-javascript • Microsoft Edge 124.0.2478.105

[√] Network resources
• All expected network resources are available.

@darshankawar darshankawar added the in triage Presently being triaged by the triage team label May 17, 2024
@darshankawar
Copy link
Member

Thanks for the report @YashExpert10
Can you narrow down code sample only to minimal and filter out unnecessary implementation to make it reduced code sample to work with ?
Also, is there a way for you to check the native Android behavior if it works as expected ? This is just to narrow down if the behavior is specific to Flutter or occurs in native as well.

@darshankawar darshankawar added the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label May 17, 2024
@YashExpert10
Copy link
Author

YashExpert10 commented May 17, 2024

Thanks for response @darshankawar
//I am Tested this code on my real Android device, I haven't tested on iOS

//Here is minimal code

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

void main() {
  runApp(const MaterialApp(home: TabsView()));
}

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

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

class _TabsViewState extends State<TabsView> {
  int _currentIndex = 0;
  int tabIndex = 0;

  void onTabTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        ///  Added Stack Widget
        children: [
          Offstage(
            offstage: _currentIndex != 0,
            child: const First(),
          ),
          Offstage(
            offstage: _currentIndex != 1,
            child: const Second(),
          ),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        selectedItemColor: Colors.deepOrange[800],
        backgroundColor: Colors.grey[300],
        unselectedItemColor: Colors.grey[800],
        currentIndex: _currentIndex,
        onTap: onTabTapped,
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Webview 1',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.web),
            label: 'Webview 2',
          ),
        ],
      ),
    );
  }
}

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

  @override
  State<First> createState() => _FirstState();
}

class _FirstState extends State<First> {
  late final WebViewController _controller1;

  @override
  void initState() {
    super.initState();

    _controller1 = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..loadRequest(Uri.parse('https://stackoverflow.com'));
  }

  Future<bool> _goBackCall1(BuildContext context) async {
    if (await _controller1.canGoBack()) {
      _controller1.goBack();
      return Future.value(false);
    } else {
      // SystemNavigator.pop();
      return Future.value(true);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Flutter WebView1"),
      ),
      body: PopScope(
        canPop: false,
        onPopInvoked: (bool didPop) {
          if (didPop) {
            print('didPop call: $didPop');
            return;
          }
          _goBackCall1(context);
        },
        child: SafeArea(
          child: Stack(
            children: [
              WebViewWidget(controller: _controller1),
            ],
          ),
        ),
      ),
    );
  }
}

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

  @override
  State<Second> createState() => _SecondState();
}

class _SecondState extends State<Second> {
  late final WebViewController _controller2;

  @override
  void initState() {
    super.initState();

    _controller2 = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..loadRequest(Uri.parse('https://google.com'));
  }

  Future<bool> _goBackCall2(BuildContext context) async {
    if (await _controller2.canGoBack()) {
      _controller2.goBack();
      return Future.value(false);
    } else {
      // SystemNavigator.pop();
      return Future.value(true);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Flutter WebView2"),
      ),      
      body: PopScope(
        canPop: false,
        onPopInvoked: (bool didPop) {
          if (didPop) {
            print('didPop call: $didPop');
            return;
          }
          _goBackCall2(context);
        },
        child: SafeArea(
          child: Stack(
            children: [
              WebViewWidget(controller: _controller2),
            ],
          ),
        ),
      ),
    );
  }
}

@github-actions github-actions bot removed the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label May 17, 2024
@stuartmorgan
Copy link
Contributor

Controller should be different for different window.
[...]
Controllers and runJavaScript and other method should not be conflict for different Class.

The issue description is describing this as being a problem where calling goBack on one controller is affecting two web views, but that not what's happening. Both onPopInvoked methods are being called when back is pressed, meaning this code is calling goBack on each webview individually. webview_flutter is therefore behavior correctly here, and the issue is with the code calling it, not the plugin.

@YashExpert10
Copy link
Author

YashExpert10 commented May 18, 2024

Controller should be different for different window.
[...]
Controllers and runJavaScript and other method should not be conflict for different Class.

The issue description is describing this as being a problem where calling goBack on one controller is affecting two web views, but that not what's happening. Both onPopInvoked methods are being called when back is pressed, meaning this code is calling goBack on each webview individually. webview_flutter is therefore behavior correctly here, and the issue is with the code calling it, not the plugin.

Thanks Sir for your Guidance @stuartmorgan

Can u please give me full example code Or Edit my example code so i understand where i am doing wrong ?

@darshankawar
Copy link
Member

@YashExpert10
Would be best to raise this in support channels like StackOverflow or https://www.reddit.com/r/flutterhelp/ to get code assistance for your case. I'll close this based on Stuart's comment.

@darshankawar darshankawar closed this as not planned Won't fix, can't repro, duplicate, stale May 20, 2024
@darshankawar darshankawar added r: invalid Issue is closed as not valid and removed in triage Presently being triaged by the triage team labels May 20, 2024
Copy link

github-actions bot commented Jun 3, 2024

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 3, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
r: invalid Issue is closed as not valid
Projects
None yet
Development

No branches or pull requests

3 participants