On Flutter...I tried to add Webview inside all the available containers that support scroll but it didn't work on Android.
ListView(
shrinkWrap: true,
children: <Widget>[
Text("hiiiiiiiiiiiii "),
Text("hiiiiiiiiiiiii "),
Text("hiiiiiiiiiiiii "),
Expanded(
child: WebView(
key: Key("webview1"),
debuggingEnabled: true,
javascriptMode: JavascriptMode.unrestricted,
initialUrl: "https://flutter.dev/")),
Text("hiiiiiiiiiiiii "),
Text("hiiiiiiiiiiiii "),
Text("hiiiiiiiiiiiii "),
],
)
If you want to put your scrollable WebView between other widget's and WebView will be scrollable, other's widget's are not, then just change your ListView for Column like that:
Column(
children: <Widget>[
Text("hiiiiiiiiiiiii "),
Text("hiiiiiiiiiiiii "),
Container(
height: 300,
child: WebView(
key: Key("webview1"),
debuggingEnabled: true,
javascriptMode: JavascriptMode.unrestricted,
initialUrl: "https://flutter.dev/")
),
Text("hiiiiiiiiiiiii "),
Text("hiiiiiiiiiiiii "),
)
But if u want to have some scrollable widgets on top of WebView, then try
CustomScrollView or SliverList.
Can you tell what exact logic you are trying to make?
additions:
If you want to have scrollable area and some WebView inside, you can have to know height of WebView:
ListView(
physics: ClampingScrollPhysics(),
children: <Widget>[
Text("hiiiiiiiiiiiii "),
Text("hiiiiiiiiiiiii "),
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 10000),
child: WebView(
gestureRecognizers: Set()
..add(
Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer(),
), // or null
),
key: Key("webview1"),
debuggingEnabled: true,
javascriptMode: JavascriptMode.unrestricted,
initialUrl: "https://flutter.dev/")),
Text("hiiiiiiiiiiiii "),
Text("hiiiiiiiiiiiii "),
],
);
Height of WebView you could get dynamically, inspiration is here:
https://gist.github.com/PonnamKarthik/877a90917a576ecff613d5169680d02c
Thank you dubace for your solution gist.github.com/PonnamKarthik/877a90917a576ecff613d5169680d02c
The issue solved and now we can add Webview inside parent scrollable container.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Code Sample',
builder: (BuildContext context, Widget navigator) {
return MyStatelessWidget();
},
);
}
}
class MyStatelessWidget extends StatefulWidget {
MyStatelessWidget({Key key}) : super(key: key);
#override
_MyStatelessWidgetState createState() => _MyStatelessWidgetState();
}
class _MyStatelessWidgetState extends State<MyStatelessWidget> {
List<WebViewController> _listController = List();
List<double> _heights =
List<double>.generate(htmlStrings.length, (int index) => 20.0);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
itemCount: htmlStrings.length,
itemBuilder: (context, index) {
return Container(
height: _heights[index] ?? 100.0,
child: WebView(
initialUrl:
'data:text/html;base64,${base64Encode(const Utf8Encoder().convert(htmlStrings[index]))}',
onPageFinished: (some) async {
double height = double.parse(await _listController[index]
.evaluateJavascript(
"document.documentElement.scrollHeight;"));
setState(() {
_heights[index] = height;
});
},
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (controller) async {
_listController.add(controller);
},
),
);
},
),
);
}
}
final List<String> htmlStrings = [
'''<p>Table shows some compounds and the name of their manufacturing process</p>
<table style="border-collapse: collapse; width: 100%; height: 85px;" border="1">
<tbody>
<tr style="height: 17px;">
<td style="width: 50%; text-align: center; height: 17px;">Compounds/Elements</td>
<td style="width: 50%; text-align: center; height: 17px;">Manufacture process</td>
</tr>
<tr style="height: 17px;">
<td style="width: 50%; height: 17px;">Nitric acid</td>
<td style="width: 50%; height: 17px;">Ostwald's process</td>
</tr>
<tr style="height: 17px;">
<td style="width: 50%; height: 17px;">Ammonia</td>
<td style="width: 50%; height: 17px;">Haber's process</td>
</tr>
<tr style="height: 17px;">
<td style="width: 50%; height: 17px;">Sulphuric acid</td>
<td style="width: 50%; height: 17px;">Contact process</td>
</tr>
<tr style="height: 17px;">
<td style="width: 50%; height: 17px;">Sodium</td>
<td style="width: 50%; height: 17px;">Down's process</td>
</tr>
</tbody>
</table>''',
'''<p>\(L=[M{L }^{2 }{T }^{-2 }{A }^{-2 }]\)</p>
<p>\(C=[{M }^{-1 }{L }^{-2 }{T }^{4 }{A }^{2 }]\)</p>
<p>\(R=[M{L }^{2 }{T }^{-3 }{A }^{-2 }]\)</p>
<p>\(\therefore \frac {R}{L}=\frac{[M{L }^{2 }{T }^{-2 }{A }^{-2 }]}{[M{L }^{2 }{T }^{-3 }{A }^{-2 }]}=[T]\)</p>''',
'''<p>Displacement(s)\(=\left(13.8\pm0.2\right)m\)</p>
<p>Time(t)\(=\left(4.0\pm0.3\right)s\)</p>
<p>Velocity(v)\(=\frac{13.8}{4.0}=3.45m{s}^{-1}\)</p>
<p>\(\frac{\delta v}{v}=\pm\left(\frac{\delta s}{s}+\frac{\delta t}{t}\right)=\pm\left(\frac{0.2}{13.8}+\frac{0.3}{4.0}\right)=\pm0.0895\)</p>
<p>\(\delta v =\pm0.0895*3.45=\pm0.3\)(rounding off to one place of decimal)</p>
<p>\(v=\left(3.45\pm0.3\right)m{s}^{-1}\)</p>''',
'''<p>The only real numbers satisfying \(x^2=4\) are 2 and -2. But none of them satisfy the final condition, \(x+2=3\). So, there is no real number such that these conditions are met. Hence this is null set.</p>
<p>Note that, for \(x\) to be a memner of \(\{x:p(x),q(x)\}\), <em><strong>both</strong></em> \(p(x)\) and \(q(x)\) should be true.</p>''',
];
First Method :-
Make a full scrollable Widget contains WebView (with dynamic height) and other containers above and below it. all should scroll inside the parent Widget
be sure flutter package:webview_flutter: any are properly install.
1.create Future fxn
String hight;
double webHight =400; //default some value
Future funcThatMakesAsyncCall() async{
var result =
await controller.evaluateJavascript('document.body.scrollHeight');
//here we call javascript for get browser data
setState(() {
hight = result;
},);}
2.we use SingleChildScrollView
scrollDirection: Axis.vertical,
3.Inside use Column
4.finally we use Conatiner
inside Container we use WebView
here Container Code
Container(
height: webHight+20,
child: WebView(
initialUrl: widget.url,
onPageFinished: (webhight)async{
await funcThatMakesAsyncCall();
setState((){
webHight= double.parse(hight);
});
},
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
controller = webViewController;
},
),
),
second method :-
plugin vesion :> webview_flutter: ^3.0.4
Column(
children: [
Container(
height: Get.height * 0.1,
color: Colors.amber,
),
Expanded(
// height: 200,
child: WebView(
gestureNavigationEnabled: true,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<OneSequenceGestureRecognizer>(
() => EagerGestureRecognizer(),
),
},
onWebViewCreated: (WebViewController webViewController) {
bscontroller.webcontroller = webViewController;
},
initialUrl: 'https://flutter.dev',
),
)
],
),
when i stuck another time than in same problem and try my old answer but this not work this time for me so, than i figure out another way
and this second answer to same problem
I had the same issue in the beginning. You can specify which gestures get passed
on to the webView widget with the gestureRecognizers parameter. Bellow code helps to capture the vertical scroll event.
WebView(
initialUrl: someUrl,
gestureRecognizers: Set()
..add(Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer())),
)
I found this post also very useful for webview
The Power of WebViews in Flutter
Related
I have a vertical scrollView then a horizontal pageView containing images wrapped inside InteractiveViewer.
but it's quite a mess when I try zooming-in with the InteractiveViewer. all those scrollviews are competing for the gestures and I was wonding an easier way to get that fixed.
Edit: So the problem seem to be with onScale gesture(GestureDetector()) or 2 pointer gestures
I have something like
CustomScrollView(
slivers: [
const SliverToBoxAdapter(
child: Container(
width: double.infinity,
height: 400,
child: PageView(
...
InteractiveViewer()
...
)
)
),
]
)
you can disable physic from relevant widget when user start to use interactive, maybe add some condition based on scale, finger, etc.
ScrollPhysics _pagePhysics = const PageScrollPhysics();
ScrollPhysics _customScrollViewPhysics = const ScrollPhysics();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: CustomScrollView(
// assign physic for scroll ------------------------
physics: _customScrollViewPhysics,
slivers: [
SliverToBoxAdapter(
child: SizedBox(
height: 400,
child: PageView(
// assign physic for page view --------------------
physics: _pagePhysics,
children: [
Container(
color: Colors.blue,
),
InteractiveViewer(
onInteractionStart: (details) {
/// you can add some condition like this
//if use 2 finger
//if(details.pointerCount == 2){}
// then disable physic
setState(() {
_pagePhysics = const NeverScrollableScrollPhysics();
_customScrollViewPhysics =
const NeverScrollableScrollPhysics();
});
},
onInteractionUpdate: (details) {
//condition based on scale
if (details.scale == 1.2) {}
},
onInteractionEnd: (details) {
// after interaction revert it
setState(() {
_pagePhysics = const PageScrollPhysics();
_customScrollViewPhysics = const ScrollPhysics();
});
},
child: Container(
color: Colors.yellow,
child: const Text("Lorem Ipsum"),
),
)
],
),
),
)
],
),
),
);
}
and if interactiveviewer leave no space for pageview or customScroll its always nice to add some button so you user dont stuck on InteractiveViewer.
you should make your ui like this :
I'm trying to implement RefreshIndicator in Webview widget in app, where RefreshIndicator not working on Webview widget. That's why I wrapped the webview widget into SingleChildScrollView then also wrapped by a container widget. RefreshIndicator displaying perfectly but problem is not able to scroll full page. It just taken screen width and height.
body: RefreshIndicator(
onRefresh: ()=>controller.reload(),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: WebView(
javascriptMode: JavascriptMode.unrestricted,
userAgent: 'random',
initialUrl: 'https://www.amazon.com/',
onWebViewCreated: (controller){
this.controller = controller;
},
),
),
),
),
How can I make it scrollable or how I will give webview width and height in container ?
I have a request to make a card containing a web view followed by more widgets. The view should look something like this.
I have made implementation like this:
SingleChildScrollView(
...
child: Column(
children: [
Container(
...
child: Column(
children: [
SizedBox(
height: _webviewHeightSetInOnLoadStop
child: InAppWebview(
...
)
),
...
)
),
Widget1(),
Widget2(),
Widget3(),
]
Where the _webviewHeightSetInOnLoadStop is set like this:
onLoadStop: (controller, url) async {
final height = await controller.evaluateJavascript(
source: "document.documentElement.scrollHeight;",
);
...
setState(() {
_webviewHeightSetInOnLoadStop = height;
...
});
}
The problem with this implementation is that when the webview is too large the Android crashes with:
IllegalStateException: Unable to create a layer for InAppWebView, size 960x39192 exceeds max size 16384
in my understanding, this is thrown due to the height of the webview, which is system restricted.
So I desire a behavior in which webview is scrollable, its container has a fixed height that is a little bit bigger than the screen (when needed) and when the end of the scroll of the webview is reached in either direction the drag event is passed to the SingleChildScrollView.
There is another plugin which can do what you are looking for exactly
webview_flutter_plus: ^0.2.3
Try this code
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter_plus/webview_flutter_plus.dart';
class ImageEditor extends StatefulWidget {
const ImageEditor({Key? key}) : super(key: key);
#override
_ImageEditorState createState() => _ImageEditorState();
}
class _ImageEditorState extends State<ImageEditor> {
WebViewPlusController? _controller;
double _height = 1;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height:MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: _height,
child: WebViewPlus(
onWebViewCreated: (controller) {
this._controller = controller; controller.loadUrl('https://pub.dev/packages/webview_flutter_plus');
},
onPageFinished: (url) {
_controller!.getHeight().then((double height) {
print("Height: " + height.toString());
setState(() {
_height = height;
});
});
},
javascriptMode: JavascriptMode.unrestricted,
),
),
Container(
height:200,
width: MediaQuery.of(context).size.width,
color: Colors.red,
),
Container(
height:200,
width: MediaQuery.of(context).size.width,
color: Colors.black,
),
Container(
height:200,
width: MediaQuery.of(context).size.width,
color: Colors.green,
),
],
),
),
),
);
}
}
You should try flutter_html instead web view
i think you should implement a custom size or just limiting it for the intended behaviour for the container of your webview and make that responsive so the screen doesn't get any crashes or like that in older devices.
I want to get the whole height of CustomScrollView widget. So I made a below code but it's not working.
#override
void initState(){
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => getSizeAndPosition());
}
getSizeAndPosition() {
RenderBox _customScrollBox =
_customScrollKey.currentContext.findRenderObject();
_customScrollSize = _customScrollBox.size;
_customScrollPosition = _customScrollBox.localToGlobal(Offset.zero);
print(_customScrollSize.height);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _customScrollKey,
appBar: _appbar(),
body: CustomScrollView(
controller: _controller,
slivers: [
SliverList(
delegate: SliverChildListDelegate([
_titleSection(),
_thumnail(),
SizedBox(
height: 40,
),
])),
],
),
);
}
The height obtained by this code does not take into account the height of the list in the customScrollview. I mean, _customScrollSize.height and MediaQuery.of(context).size.width are the same.
I want this function
_controller.addListener(() {
setState(() {
if (_controller.offset < 0) {
scrollHeight = 0;
} else {
scrollHeight = _controller.offset;
}
});
});
Container(
width: size.width * (scrollHeight / _customScrollSize.height),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1)),
With the above code, '_customScrollSize.height' does not reflect the overall height and therefore the function is not implemented properly. Is there any good way to use it in this situation?
CustomScrollView
A ScrollView that creates custom scroll effects using slivers.
A CustomScrollView lets you supply slivers directly to create various scrolling effects, such as lists, grids, and expanding headers. For example, to create a scroll view that contains an expanding app bar followed by a list and a grid, use a list of three slivers: SliverAppBar, SliverList, and SliverGrid.
In your case do remove the appBar and use a sliverAppBar withing the custome scrollview , and you can use sliverfillRemaining widget for your other children
example
CustomScrollView(
slivers: <Widget>[
const SliverAppBar(
pinned: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('Demo'),
),
),
SliverList(
delegate: SliverChildListDelegate([
_titleSection(),
_thumnail(),
SizedBox(
height: 40,
),
])),
...
I'm trying to make an event page for an app where user can view events that have a banner image and some other useful information. I really like the idea of implementing a SliverAppBar with the banner, so that the user can scroll to see more information. For this I seem to need a CustomScrollView with a SliverAppBar and FlexibleSpaceBar.
All tutorials I have seen online assume that the rest of the screen should be a list of sorts, but I rather want something like a Column widget. A Column has unbounded height, however, which causes overflow errors in the CustomScrollView. I could wrap it in a Container with specified height, but the contents of the body are of variable size, so that is not ideal. Is there a way to have a SliverAppBar and a Column work side by side?
I want something along the lines of this:
class ActivityPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(slivers: [
SliverAppBar(
flexibleSpace: FlexibleSpaceBar(
background: Image(someImage),
),
expandedHeight: Image,
floating: false,
pinned: true,
snap: false,
),
Column(
children: [
someChildren,
]
),
)
]),
),
);
}
It should be possible, because it seems to me a somewhat common pattern, but I have looked around a lot and I can only find examples where the body consists of lists...
For anyone having the same struggle: here's the solution I just found:
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
backgroundColor: this.color,
flexibleSpace: FlexibleSpaceBar(
background: YourImage(),
),
)
];
},
body: Container(
child: Builder(builder: (context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
WidgetOne(),
WidgetTwo()
]);
})),
),
)),
);
}
Use SliverList and SliverChildListDelegate instead of a Column.
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
background: Container(color: Colors.green),
),
),
SliverList(
delegate: SliverChildListDelegate([
Container(color: Colors.yellow, height: 400),
Container(color: Colors.red, height: 800),
]),
),
],
),
);
}
Use ListView instead of Column. ListView has dynamic size
For me the best way is to use SliverToBoxAdapter. Just wrap your Column in a Container and then wrap this Container in a SliverToBoxAdapter and it should work fine.