In my flutter project I made a floating button that help to auto scroll to the top of the page with one click, and when it reach the top it disappear. It work perfectly. But my problem is that I need to double click in it so it can disappear I want it to automatically disappear if it reach the top. Any help is highly appreciated.
void scrollToTop(){
_controller.runJavascript("window.scrollTo({top: 0, behavior: 'smooth'});");
floatingButtonVisibility();
}
void floatingButtonVisibility() async {
int y = await _controller.getScrollY();
if(y>50){
setState(() {
buttonshow = true;
});
}else {
setState(() {
buttonshow = false;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView'),
),
body: WebView(
initialUrl: 'https://flutter.dev',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller = webViewController;
},
gestureRecognizers: Set()
..add(
Factory<VerticalDragGestureRecognizer>(() => VerticalDragGestureRecognizer()
..onDown = (tap) {
floatingButtonVisibility();
}))
),
floatingActionButton: Visibility(
visible: buttonshow,
child: FloatingActionButton(
onPressed: () {
scrollToTop();
},
backgroundColor: Colors.blue,
child: const Icon(Icons.navigation),
),
),
);
}
}
Here is my solution.
Register 'window.onscroll' in to send webview's scroll position to outside of Webview widget.
Register receiver to receive event from webview.
If scroll is 0, change 'buttonshow' value and rebuild widget.
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
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 Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
WebViewController _controller;
bool buttonshow = false;
#override
void initState() {
super.initState();
}
void scrollToTop() {
_controller.evaluateJavascript(
"window.onscroll = function () {scrollEventChannel.postMessage(window.scrollY)};");
_controller
.evaluateJavascript("window.scrollTo({top: 0, behavior: 'smooth'});");
floatingButtonVisibility();
}
void floatingButtonVisibility() async {
int y = await _controller.getScrollY();
if (y > 50) {
setState(() {
buttonshow = true;
});
} else {
setState(() {
buttonshow = false;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView'),
),
body: WebView(
initialUrl: 'https://flutter.dev',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller = webViewController;
},
javascriptChannels: {
JavascriptChannel(
name: 'scrollEventChannel',
onMessageReceived: (JavascriptMessage message) {
print('>>>>: ${message.message}');
if (message.message == '0') {
setState(() {
buttonshow = false;
});
}
}),
},
gestureRecognizers: Set()
..add(Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer()
..onDown = (tap) {
floatingButtonVisibility();
}))),
floatingActionButton: Visibility(
visible: buttonshow,
child: FloatingActionButton(
onPressed: () {
scrollToTop();
},
backgroundColor: Colors.blue,
child: const Icon(Icons.navigation),
),
),
);
}
}
I changed button show trigger from gestureRecognizers to postion event.
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
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 Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
WebViewController _controller;
bool buttonshow = false;
#override
void initState() {
super.initState();
}
void scrollToTop() {
_controller
.evaluateJavascript("window.scrollTo({top: 0, behavior: 'smooth'});");
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView'),
),
body: WebView(
initialUrl: 'https://flutter.dev',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller = webViewController;
},
onPageFinished: (String url) async {
_controller.evaluateJavascript(
"window.onscroll = function () {scrollEventChannel.postMessage(window.scrollY)};");
},
javascriptChannels: {
JavascriptChannel(
name: 'scrollEventChannel',
onMessageReceived: (JavascriptMessage message) {
print('>>>>: ${message.message}');
int position = int.parse(message.message);
if (position == 0) {
setState(() {
buttonshow = false;
});
} else if (position > 60) {
setState(() {
buttonshow = true;
});
}
}),
},
),
floatingActionButton: Visibility(
visible: buttonshow,
child: FloatingActionButton(
onPressed: () {
scrollToTop();
},
backgroundColor: Colors.blue,
child: const Icon(Icons.navigation),
),
),
);
}
}
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget{
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Home()
);
}
}
class Home extends StatefulWidget {
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
ScrollController scrollController = ScrollController();
bool showbtn = false;
List<String> countries = ["USA", "United Kingdom", "China", "Russia", "Brazil",
"India", "Pakistan", "Nepal", "Bangladesh", "Sri Lanka",
"Japan", "South Korea", "Mongolia"];
#override
void initState() {
scrollController.addListener(() { //scroll listener
double showoffset = 10.0; //Back to top botton will show on scroll offset 10.0
if(scrollController.offset > showoffset){
showbtn = true;
setState(() {
//update state
});
}else{
showbtn = false;
setState(() {
//update state
});
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Scroll Back to Top Button"),
backgroundColor: Colors.redAccent
),
floatingActionButton: AnimatedOpacity(
duration: Duration(milliseconds: 1000), //show/hide animation
opacity: showbtn?1.0:0.0, //set obacity to 1 on visible, or hide
child: FloatingActionButton(
onPressed: () {
scrollController.animateTo( //go to top of scroll
0, //scroll offset to go
duration: Duration(milliseconds: 500), //duration of scroll
curve:Curves.fastOutSlowIn //scroll type
);
},
child: Icon(Icons.arrow_upward),
backgroundColor: Colors.redAccent,
),
),
body: SingleChildScrollView(
controller: scrollController, //set controller
child:Container(
child:Column(
children: countries.map((country){
return Card(
child:ListTile(
title: Text(country)
)
);
}).toList(),
)
)
)
);
}
This type of dropdown.. I have tried more times in using a ListTile widget.
You have to use a RadioListTile for this purpose. this code is from the flutter documentation, you can try it:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
enum SingingCharacter { lafayette, jefferson }
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
SingingCharacter? _character = SingingCharacter.lafayette;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
RadioListTile<SingingCharacter>(
title: const Text('Lafayette'),
value: SingingCharacter.lafayette,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
},
),
RadioListTile<SingingCharacter>(
title: const Text('Thomas Jefferson'),
value: SingingCharacter.jefferson,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
},
),
],
);
}
}
I am using Webview with WillPopScope, webview internal back button is working fine but on the first page back press is not working.
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SafeArea(child: MyHomePage()),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
InAppWebViewController _controller;
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => canGoBack(context),
child: Scaffold(
body: InAppWebView(
initialUrlRequest:
URLRequest(url: Uri.parse("https://www.google.com/")),
onWebViewCreated: onWebviewCreated,
),
),
);
}
void onWebviewCreated(InAppWebViewController controller) {
_controller = controller;
}
canGoBack(BuildContext context) async {
if (await _controller.canGoBack()) {
_controller.goBack();
return Future.value(false);
} else {
return Future.value(true);
}
}
}
tried with
canGoBack(BuildContext context) async {
if (await _controller.canGoBack()) {
_controller.goBack();
return Future.value(false);
} else {
Navigator.pop(context, true);
return Future.value(false);
}
}
the issue is showing the black if there is no webview history.
You don't have any previous root and you're using
Navigator.pop(context, true); // in canGoBack function
That's cause to issue. Remove that line and it will work.
Code
import 'package:flutter/foundation.dart';
import 'package:web_socket_channel/io.dart';
import 'package:flutter/material.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final title = 'WebSocket Demo';
return MaterialApp(
title: title,
home: MyHomePage(
title: title,
channel: IOWebSocketChannel.connect('ws://192.168.0.164:6001/app/KEY'),
),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
final WebSocketChannel channel;
MyHomePage({Key key, #required this.title, #required this.channel}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: StreamBuilder(
stream: widget.channel.stream,
builder: (context, snapshot) {
if(snapshot.hasError)
print(snapshot.error);
else {
widget.channel.sink.add("123");
print("XXX : ${snapshot.data}");
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Text(snapshot.hasData ? '${snapshot.data}' : ''),
);
},
),
),
);
}
#override
void dispose() {
widget.channel.sink.close();
super.dispose();
}
}
The output of the Print Statement is
XXX : {"event":"pusher:connection_established","data":"{"socket_id":"943971557.707744157","activity_timeout":30}"}
It does not fetch refreshed data as the Event and Channel isn't mentioned for my laravel backend. Is there a way that can be mentioned or have i got it wrong somewhere else?
I am trying to create a splash screen in my project but this code is not working. it just shows the splash screen forever. it doesn't switch to the home screen. Any help? (I want to create an app that starts with a splash screen but i don't want this splash screen to be a photo)
import 'dart:async';
void main() {
runApp( splash(),);
}
// ignore: camel_case_types
class splash extends StatefulWidget {
#override
_splashState createState() => _splashState();
}
// ignore: camel_case_types
class _splashState extends State<splash> {
#override
void initState() {
super.initState();
Timer(
Duration(seconds: 5),
() => Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => home(),),),);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(child: Text("Splash Screen"),)
),
);
}
}
class home extends StatefulWidget {
#override
_homeState createState() => _homeState();
}
// ignore: camel_case_types
class _homeState extends State<home> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Home"),
),
),
);
}
}```
Separate your MaterialApp from your splash
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp(),);
}
class Splash extends StatefulWidget {
#override
_SplashState createState() => _SplashState();
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Splash(),
);
}
}
class _SplashState extends State<Splash> {
#override
void initState() {
super.initState();
Timer(
Duration(seconds: 5),
() => Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => Home(),),),);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text("Splash Screen"),)
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Home"),
),
),
);
}
}