Flutter || Scroll back to top button - android

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

Related

Cannot resolve symbol '#android:color/white'

I'm having this issue while creating a splash screen in flutter. I looked for an answer but no one solve the matter or answer perfectly.
Cannot resolve symbol '#android:color/black'
Create StateFulWidget
Add one Future.delayed() to initstate.
#override
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 3), () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const SecondScreen(),
),
);
});
}
Duration(seconds:3) will wait for 3 seconds on the splash screen then it will redirect to SecondScreen.
the code should be like this
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 3), () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const SecondScreen(),
),
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.blue,
child: const Center(child: Text("Splash Screen")),
),
);
}
}
the whole code
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const SplashScreen(),
);
}
}
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 3), () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const SecondScreen(),
),
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.blue,
child: const Center(child: Text("Splash Screen")),
),
);
}
}
class SecondScreen extends StatefulWidget {
const SecondScreen({super.key});
#override
State<SecondScreen> createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.green,
child: const Center(
child: Text("Home Screen"),
),
),
);
}
}
If it was useful, you can choose it as an approved answer and give points. Good coding.
Try using this,
<item android:background="#android:color/white" />
When it comes to drawable you should assign XMLs or images in your drawable or mipmap resource

renderanimatedopacity object was given an infinite size during layout

Using this code brings me this warning when I run :
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xffebebeb),
body: AnimatedCrossFade(
firstChild: SplashScreen1(),
secondChild: SplashScreen2(),
crossFadeState:
!phaseTwo ? CrossFadeState.showFirst : CrossFadeState.showSecond,
duration: Duration(seconds: 1),
)
);
}
SplashScreen1()
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xffebebeb),
);
}
SplashScreen2()
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xffebebeb),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Image.asset('assets/images/splash_image_1.png'),
],
),
);
}
You can copy paste run full code below
You can replace Scaffold of SplashScreen1 and SplashScreen2 with Container
code snippet
class SplashScreen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: const Color(0xffebebeb),
);
}
}
working demo
full code
import 'package:flutter/material.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> {
bool phaseTwo = false;
#override
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 3), () {
setState(() {
phaseTwo = true;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xffebebeb),
body: AnimatedCrossFade(
firstChild: SplashScreen1(),
secondChild: SplashScreen2(),
crossFadeState:
!phaseTwo ? CrossFadeState.showFirst : CrossFadeState.showSecond,
duration: Duration(seconds: 1),
));
}
}
class SplashScreen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: const Color(0xffebebeb),
);
}
}
class SplashScreen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: const Color(0xffebebeb),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Image.network('https://picsum.photos/250?image=9'),
],
),
);
}
}

Unhandled Exception: No MediaQuery widget found - flutter ShowModalButtonSheet

I'm working on this project to show a showModalBottomSheet widget after pressing on markers (current location) of Google Maps, using onTap() method of Marker. But it's showing Exception: MyApp widgets require a MediaQuery widget ancestor.
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
GoogleMapController mapController;
Position currentLocation;
LatLng _center;
Set<Marker> _markers = Set();
#override
void initState() {
// TODO: implement initState
super.initState();
setLocation();
}
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
}
void setLocation() async {
currentLocation = await Geolocator()
.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
setState(() {
_center = LatLng(currentLocation.latitude, currentLocation.longitude);
});
}
Widget googleMap() {
addMarkers();
return GoogleMap(
onMapCreated: _onMapCreated,
myLocationEnabled: true,
initialCameraPosition: CameraPosition(
target: _center,
),
markers: _markers,
);
}
void addMarkers() {
_markers.addAll([
Marker(
markerId: MarkerId('current location'),
position: _center,
onTap: () {
print("tapped !!!!!!");
showModalBottomSheet(
context: context,
builder: (context) {
return Text('Modal bottom sheet', style: TextStyle(fontSize: 30));
});
},
),
]);
}
#override
Widget build(BuildContext context) {
print("Debug: build called!!!");
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Google Map'),
backgroundColor: Colors.blue,
),
body: (_center == null)
? Center(child: CircularProgressIndicator())
: googleMap(),
),
);
}
}
How can I solve this?
This worked for me. I renamed the MyApp StatefulWidget to Home and created a StatelessWidget and named it as MyApp. Then I passed the Home StatefulWidget to the home attribute of MyApp StatelessWidget.
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Home());
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
GoogleMapController mapController;
Position currentLocation;
LatLng _center;
Set<Marker> _markers = Set();
#override
void initState() {
// TODO: implement initState
super.initState();
setLocation();
}
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
}
void setLocation() async {
currentLocation = await Geolocator()
.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
setState(() {
_center = LatLng(currentLocation.latitude, currentLocation.longitude);
});
}
Widget googleMap() {
addMarkers();
return GoogleMap(
onMapCreated: _onMapCreated,
myLocationEnabled: true,
initialCameraPosition: CameraPosition(
target: _center,
),
markers: _markers,
);
}
void addMarkers() {
_markers.addAll([
Marker(
markerId: MarkerId('current location'),
position: _center,
onTap: () {
print("tapped !!!!!!");
showModalBottomSheet(
context: context,
builder: (context) {
return Center(
child: Text('Modal bottom sheet',
style: TextStyle(fontSize: 30)),
);
});
},
),
]);
}
#override
Widget build(BuildContext context) {
print("Debug: build called!!!");
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Google Map'),
backgroundColor: Colors.blue,
),
body: (_center == null)
? Center(child: CircularProgressIndicator())
: googleMap(),
),
);
}
}
I solve this problem using returning google map widget inside Builder & referencing the builder context
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'package:android_intent/android_intent.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
GoogleMapController mapController;
LatLng _currentPosition;
Set<Marker> _markers = Set();
// Obtaining current location coordinate & setting it
void setLocation() async {
bool isLocationEnabled = await Geolocator().isLocationServiceEnabled();
if(!isLocationEnabled){
openLocationSetting();
}
Position currentLocation = await Geolocator()
.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
setState(() {
_currentPosition =
LatLng(currentLocation.latitude, currentLocation.longitude);
});
}
// Open Location Setting if GPS not enabled
void openLocationSetting() async {
final AndroidIntent intent = new AndroidIntent(
action: 'android.settings.LOCATION_SOURCE_SETTINGS',
);
await intent.launch();
}
// initializing _currentPosition state.
#override
void initState() {
// TODO: implement initState
super.initState();
// Initiate full screen
SystemChrome.setEnabledSystemUIOverlays([]);
// Initiate current location
setLocation();
}
void showModal(BuildContext context) {
final _formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final locationController = TextEditingController();
locationController.text = _currentPosition.latitude.toString() +
"," +
_currentPosition.longitude.toString();
// Showing modal with Name & Current location input form to send data to console
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: Form(
key: _formKey,
child: Wrap(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
hintText: 'Enter your current location',
),
controller: locationController,
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
TextFormField(
decoration: InputDecoration(
hintText: 'Enter your name',
),
controller: nameController,
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
RaisedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
debugPrint(nameController.text);
debugPrint(locationController.text);
}
},
child: Text('Send'),
)
],
),
),
));
});
}
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
}
// Adding markers in Google map for current location
void addMarkers(BuildContext context) {
_markers.addAll([
Marker(
markerId: MarkerId('Current Location'),
position: _currentPosition,
onTap: () {
showModal(context);
},
),
]);
}
// Building Google map widget & pointing initial position to current position
Widget googleMap(BuildContext context) {
addMarkers(context);
return GoogleMap(
onMapCreated: _onMapCreated,
myLocationEnabled: true,
initialCameraPosition: CameraPosition(
target: _currentPosition,
),
markers: _markers,
);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Google Map'),
backgroundColor: Colors.teal,
),
body: Builder(
builder: (context) {
return (_currentPosition == null)
? Center(child: CircularProgressIndicator(),)
: googleMap(context);
},
)),
);
}
}

Even though the backbutton close is implemented, it does not work

Even though I implemented backbutton App close. If you press the backbutton, no message appears and you are returned to the login screen.
The homepage is running in the homescreen.
In order to find any possible errors, I put the backbutton close code in both places.
I am not sure what is wrong.
I need help.
HomeScreen
class HomeScreen extends StatelessWidget {
DateTime currentBackPressTime;
final scaffoldKey = GlobalKey<ScaffoldState>();
final FirebaseUser user;
HomeScreen({this.user});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Custom Navigation Drawer Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: WillPopScope(
onWillPop: () async {
bool result = onPressBackButton();
return await Future.value(result);
},
child: MyHomePage(),
),
);
}
bool onPressBackButton() {
DateTime now = DateTime.now();
if (currentBackPressTime == null ||
now.difference(currentBackPressTime) > Duration(seconds: 2)) {
currentBackPressTime = now;
scaffoldKey.currentState
..hideCurrentSnackBar()
..showSnackBar(SnackBar(
content: Text("Tap back again to leave."),
));
return false;
}
return true;
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
scaffoldBackgroundColor: Colors.white,
primaryColor: Colors.white
),
home: SideBarLayout(),
);
}
}
HomeScreen
import 'package:flutter/material.dart';
import 'package:aciel_pro/navigation_bloc/navigation_bloc.dart';
class Home extends StatelessWidget {
DateTime currentBackPressTime;
final scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Custom Navigation Drawer Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: WillPopScope(
onWillPop: () async {
bool result = onPressBackButton();
return await Future.value(result);
},
child: HomePage(),
),
);
}
bool onPressBackButton() {
DateTime now = DateTime.now();
if (currentBackPressTime == null ||
now.difference(currentBackPressTime) > Duration(seconds: 2)) {
currentBackPressTime = now;
scaffoldKey.currentState
..hideCurrentSnackBar()
..showSnackBar(SnackBar(
content: Text("Tap back again to leave."),
));
return false;
}
return true;
}
}
class HomePage extends StatelessWidget with NavigationStates {
BuildContext ctx;
#override
Widget build(BuildContext context) {
ctx = context;
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
FlatButton(
child: Text('예약하기',style: TextStyle(fontSize: 40)),
onPressed: () =>showMessage('예약하기'),
color:Colors.green,
textColor: Colors.white,
)
],
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
),
),
);
}
void showMessage(String msg) {
final snackbar = SnackBar(content: Text(msg));
Scaffold.of(ctx)
..removeCurrentSnackBar()
..showSnackBar(snackbar);
}
}
You can copy paste run full code below
Step 1 : move final scaffoldKey = GlobalKey<ScaffoldState>(); out of Home
Step 2 : add Scaffold key
return Scaffold(
key: scaffoldKey,
Step 3: showMessage() use scaffoldKey
scaffoldKey.currentState
..removeCurrentSnackBar()
..showSnackBar(snackbar);
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(Home());
}
final scaffoldKey = GlobalKey<ScaffoldState>();
class Home extends StatelessWidget {
DateTime currentBackPressTime;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Custom Navigation Drawer Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: WillPopScope(
onWillPop: () async {
bool result = onPressBackButton();
return await Future.value(result);
},
child: HomePage(),
),
);
}
bool onPressBackButton() {
DateTime now = DateTime.now();
if (currentBackPressTime == null ||
now.difference(currentBackPressTime) > Duration(seconds: 2)) {
currentBackPressTime = now;
scaffoldKey.currentState
..hideCurrentSnackBar()
..showSnackBar(SnackBar(
content: Text("Tap back again to leave."),
));
return false;
}
return true;
}
}
class HomePage extends StatelessWidget {
BuildContext ctx;
#override
Widget build(BuildContext context) {
ctx = context;
return Scaffold(
key: scaffoldKey,
body: Center(
child: Column(
children: <Widget>[
FlatButton(
child: Text('예약하기', style: TextStyle(fontSize: 40)),
onPressed: () => showMessage('예약하기'),
color: Colors.green,
textColor: Colors.white,
)
],
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
),
),
);
}
void showMessage(String msg) {
final snackbar = SnackBar(content: Text(msg));
scaffoldKey.currentState
..removeCurrentSnackBar()
..showSnackBar(snackbar);
}
}

Flutter - How correctly pause camera when user moved to other (preview) screen?

I need to pause camera when I move to another screen on the navigator tree in order to save battery and performance.
I tried to dispose() cameraController, but flutter doesn't re-initialize the state when it returns from another screen (which is obvious, though).
My main code to work with a camera:
#override
void initState() {
super.initState();
availableCameras().then((cameras) {
setState(() {
_firstCamera = cameras.first;
_controller = CameraController(_firstCamera, ResolutionPreset.high);
_initializeControllerFuture = _controller.initialize();
});
});
}
#override
void dispose() {
_controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: Stack(
children: <Widget>[
FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Stack(
alignment: FractionalOffset.center,
children: <Widget>[
new Positioned.fill(
child: _getCameraPreview(context),
),
...
],
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
Align(
alignment: Alignment.bottomCenter,
child: BottomAppBar(
color: Color.fromARGB(0, 0, 0, 0),
child: _getBottomAppBarRow(context),
),
),
],
),
);
}
_getCameraPreview(BuildContext context) {
final size = MediaQuery.of(context).size;
final deviceRatio = size.width / size.height;
return Transform.scale(
scale: _controller.value.aspectRatio / deviceRatio,
child: Center(
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: CameraPreview(_controller),
),
),
);
}
Have a variable like _cameraOn = true. Show CameraPreview when it is true and not when it is false. While navigating to another screen set it to false
You could have the camera related functionality in a separate widget. So every time it is displayed it is initialized, and when it is not it's disposed.
A simple working example
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
List<CameraDescription> cameras;
Future<void> main() async {
cameras = await availableCameras();
runApp(MaterialApp(
home: CameraApp(),
));
}
class CameraApp extends StatefulWidget {
#override
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
bool _cameraOn = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: _cameraOn ? Camera() : Container(),
),
FlatButton(
onPressed: () {
setState(() {
_cameraOn = false;
});
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => Post())).then((res) {
setState(() {
_cameraOn = true;
});
}).catchError((err) {
print(err);
});
},
child: Text("NEXT PAGE"),
),
],
),
);
}
}
class Camera extends StatefulWidget {
#override
_CameraState createState() => _CameraState();
}
class _CameraState extends State<Camera> {
CameraController controller;
#override
void initState() {
super.initState();
controller = CameraController(cameras[0], ResolutionPreset.medium);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
}
#override
Widget build(BuildContext context) {
if (!controller.value.isInitialized) {
return Container();
}
return AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: CameraPreview(controller),
);
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
}
class Post extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text("Post"),
);
}
}
Suppose the camera controller for an instance of the camera package is defined as such:
List<CameraDescription> cameras = [];
controller = CameraController(
cameras[0],
ResolutionPreset.high,
enableAudio: false,
);
This can be used to pause the camera:
controller.pausePreview();
This can be used to resume the camera:
controller.resumePreview();

Categories

Resources