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);
},
)),
);
}
}
Related
I am building a mobile application using flutter. The app has a homepage with an AnimatedIndexedStack. To switch to another screen I simply change the index. If I run the app, I can switch to any screen without any problem. However, if I switch to a screen with a textfield and tap on the textfield to edit. Now after cancelling the keyboard, if I switch to any other screen, the keyboard pops up automatically when there are no textfields on the screens and I am not tapping on any textfield. Regardless of the start screen and the end screen, the keyboard pops up automatically after changing the index.
The animated indexed stack looks like this:
import 'package:flutter/material.dart';
class AnimatedIndexedStack extends StatefulWidget {
final int index;
final List<Widget> children;
const AnimatedIndexedStack({
Key? key,
required this.index,
required this.children,
}) : super(key: key);
#override
_AnimatedIndexedStackState createState() => _AnimatedIndexedStackState();
}
class _AnimatedIndexedStackState extends State<AnimatedIndexedStack>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
int _index = 0;
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 250),
);
_animation = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.ease,
),
);
_index = widget.index;
_controller.forward();
super.initState();
}
#override
void didUpdateWidget(AnimatedIndexedStack oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.index != _index) {
_controller.reverse().then((_) {
setState(() => _index = widget.index);
_controller.forward();
});
}
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Opacity(
opacity: _controller.value,
child: child,
);
},
child: IndexedStack(
index: _index,
children: widget.children,
),
);
}
}
homepage looks like this:
class Homepage extends StatefulWidget {
const Homepage({Key? key}) : super(key: key);
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
var _currentIndex = 0;
final _homeIndex = 0;
final _titles = ['Home', 'New Booking', 'Trips', 'Payment History', 'Support', 'Profile'];
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
var _bookingStep = 0;
#override
void initState() {
_currentIndex = _homeIndex;
super.initState();
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => _onWillPop(),
child: Scaffold(
key: scaffoldKey,
extendBody: true,
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
drawer: AppDrawer(selectedIndex: _currentIndex, onItemTap: (index) {
setState(() {
_currentIndex = index;
});
scaffoldKey.currentState!.closeDrawer();
},),
body: SafeArea(
bottom: false,
child: AnimatedIndexedStack(
index: _currentIndex,
children: [
const BookingsScreen(),
NewBookingScreen(onExit: () {
setState(() {
_currentIndex = 0;
});
}, bookingStep: _bookingStep, nextStep: () {
setState(() {
_bookingStep = _bookingStep + 1;
});
},),
const Trips(),
const PaymentHistoryScreen(),
SupportScreen(),
const ProfileScreen()
],
)
),
),
);
}
}
Try adding this to all screens where you have the issue,
FocusManager.instance.primaryFocus?.unfocus()
I have this page
I want when Scroll reach the end, load more data from server.
my Code is here:
class MyProfilePage extends StatefulWidget {
late String storeCode;
bool hasBack = true;
MyProfilePage(this.storeCode, this.hasBack);
#override
_MyProfilePageState createState() => _MyProfilePageState();
}
class _MyProfilePageState extends State<MyProfilePage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
late ScrollController _scrollController;
bool finish = false;
int page = 1;
#override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
_scrollController = ScrollController();
context.read<FavoriteListCubit>().getPostList(widget.storeCode);
context.read<PostsListCubit>().getPostList(widget.storeCode);
context.read<TagMenuCubit>().getTagMenuList(widget.storeCode);
// }
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
if (mounted) {
if (!finish) {
////this condition check if not requested,request again
if (context.read<PostsListCubit>().state is! PostsMoreListLoading) {
print("pageeee");
page = page + 1;
context.read<PostsListCubit>().getMorePostList(widget.storeCode,page);
}
}
}
} else {}
});
}
#override
void dispose() {
_tabController.dispose();
_scrollController.dispose();
super.dispose();
}
late BuildContext ctx;
Widget? header;
Widget? body_;
#override
Widget build(BuildContext context) {
ctx = context;
body_ ??= buildProfile(context);
return body_!;
}
Widget buildProfile(BuildContext context) {
return Scaffold(
appBar: widget.hasBack ? MyAppBar().appBarFirstMenu("title_", context, widget.hasBack) : null,
body: SafeArea(
child: Directionality(
textDirection: TextDirection.ltr,
child: DefaultTabController(
length: _tabController.length,
child: RefreshIndicator(
notificationPredicate: (notification) {
return notification.depth == 2;
},
onRefresh: () async {
context.read<StoreInfoCubit>().getStoreInfo(widget.storeCode);
context.read<PostsListCubit>().getPostList(widget.storeCode);
context.read<TagMenuCubit>().getTagMenuList(widget.storeCode);
return context.read<FavoriteListCubit>().getPostList(widget.storeCode);
},
child: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverToBoxAdapter(
child: header ?? (header = MyInfo(
storeCode: widget.storeCode,
))),
SliverPersistentHeader(
delegate: _CustomSliverDelegate(_tabController),
pinned: true,
floating: true,
),
];
},
body: TabBarView(
controller: _tabController,
children: [
PostListGridView(storeCode:widget.storeCode),
PlayListGridView(storeCode: widget.storeCode),
FavoriteListGridView(storeCode:widget.storeCode),
],
),
),
),
),
),
),
);
}
Widget playTab() {
return Center( child: SingleChildScrollView(
child: Text(MyString.commingSoon),
),
);
}
}
class _CustomSliverDelegate extends SliverPersistentHeaderDelegate {
TabController _tabController;
_CustomSliverDelegate(this._tabController);
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
height: 50.0,
color: Colors.grey[50],
child: TabBar(
controller: _tabController,
tabs: const [
Icon(Icons.grid_on),
Icon(CupertinoIcons.play),
Icon(FontAwesome.bookmark_empty),
],
),
);
}
#override
double get maxExtent => 50.0;
#override
double get minExtent => 50.0;
#override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return false;
// throw UnimplementedError();
}
}
I try set controller for NestedScrollView but not Working good, try to set Controller for GridView this way relatively good but causes that page has two Scroll.(once for nestedScrollView and once for gridview)
how to solve this problem?
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(),
)
)
)
);
}
In my program I put the add button to create stateful box with stateful drop down button inside of it, each time I add the box I add it to Map<int, Widget> and pass it to the column. When I click on the cross button it delete the widget from the map in parent. But when I click on cross button on the widget, it show wrong colour of the box and wrong drop down value.Watch the GIF I posted to get the overview of the problem
Link to dart pad to run the example : dart pad code link here
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_StateMyWidget createState() => _StateMyWidget();
}
class _StateMyWidget extends State<MyWidget> {
Map<int, Widget> widgetList = {};
int boxCount = 0;
#override
initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return ListView(children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: widgetList.values.toList()),
TextButton(
onPressed: () {
widgetList[boxCount] =
new MyBox(boxIndex: boxCount, deleteFunction: deleteBox);
setState(() {});
boxCount += 1;
},
child: Text("Add"))
],
)
]);
}
deleteBox(boxIndex) {
widgetList.remove(boxIndex);
setState(() {});
}
}
class MyBox extends StatefulWidget {
final int boxIndex;
final Function deleteFunction;
MyBox({required this.boxIndex, required this.deleteFunction});
_StateMyBox createState() => _StateMyBox();
}
class _StateMyBox extends State<MyBox> {
var containerColor;
#override
initState() {
super.initState();
containerColor =
Colors.primaries[Random().nextInt(Colors.primaries.length)];
}
#override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 200,
margin: EdgeInsets.all(17),
padding: EdgeInsets.all(10),
color: containerColor,
child: Column(children: [
Row(children: [
Text("Box Number: ${widget.boxIndex}"),
Spacer(),
IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
widget.deleteFunction(widget.boxIndex);
},
),
]),
RegistrationDropdown(listData: ['One', 'Two', 'Three', 'Four']),
]));
}
}
class RegistrationDropdown extends StatefulWidget {
final List<String> listData;
RegistrationDropdown({
required this.listData,
});
#override
_StateRegistrationDropdown createState() {
return _StateRegistrationDropdown();
}
}
class _StateRegistrationDropdown extends State<RegistrationDropdown> {
String dropdownValue = 'One';
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
padding: EdgeInsets.only(left: 10, right: 10),
child: DropdownButton<String>(
isExpanded: true,
underline: SizedBox(),
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
onChanged: (String? newValue) {
print("Previous dropdown value $dropdownValue");
print("New value $newValue");
setState(() {
dropdownValue = newValue!;
});
},
items: widget.listData.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
));
}
}
The solution is a Key of the widget. The When to Use Keys: Flutter Youtube will be helpful.
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_StateMyWidget createState() => _StateMyWidget();
}
class _StateMyWidget extends State<MyWidget> {
Map<int, Widget> widgetList = {};
int boxCount = 0;
#override
initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return ListView(
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: widgetList.values.toList(),
),
TextButton(
onPressed: () {
widgetList[boxCount] = new MyBox(
key: UniqueKey(), // <---------------------
boxIndex: boxCount,
deleteFunction: deleteBox,
);
setState(() {});
boxCount += 1;
},
child: Text("Add"),
)
],
)
],
);
}
deleteBox(boxIndex) {
widgetList.remove(boxIndex);
setState(() {});
}
}
class MyBox extends StatefulWidget {
final int boxIndex;
final Function deleteFunction;
MyBox({
Key? key, // <---------------------
required this.boxIndex,
required this.deleteFunction,
}) : super(key: key); // <---------------------
_StateMyBox createState() => _StateMyBox();
}
class _StateMyBox extends State<MyBox> {
var containerColor;
#override
initState() {
super.initState();
containerColor =
Colors.primaries[Random().nextInt(Colors.primaries.length)];
}
#override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 200,
margin: EdgeInsets.all(17),
padding: EdgeInsets.all(10),
color: containerColor,
child: Column(children: [
Row(children: [
Text("Box Number: ${widget.boxIndex}"),
Spacer(),
IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
widget.deleteFunction(widget.boxIndex);
},
),
]),
RegistrationDropdown(listData: ['One', 'Two', 'Three', 'Four']),
]));
}
}
class RegistrationDropdown extends StatefulWidget {
final List<String> listData;
RegistrationDropdown({
required this.listData,
});
#override
_StateRegistrationDropdown createState() {
return _StateRegistrationDropdown();
}
}
class _StateRegistrationDropdown extends State<RegistrationDropdown> {
String dropdownValue = 'One';
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
padding: EdgeInsets.only(left: 10, right: 10),
child: DropdownButton<String>(
isExpanded: true,
underline: SizedBox(),
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
onChanged: (String? newValue) {
print("Previous dropdown value $dropdownValue");
print("New value $newValue");
setState(() {
dropdownValue = newValue!;
});
},
items: widget.listData.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
);
}
}
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();