Why stateful widget unable to maintain it's state in flutter - android

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

Related

how to load more data when reach in end of list in NestedScrollView in flutter

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?

Flutter Switch doesn't work in Modalbottomsheet

My setup:
class Start_Page extends StatefulWidget {
#override
StartPageState createState() => StartPageState();
}
class StartPageState extends State<Start_Page> {
#override
Widget build(BuildContext context){
return Scaffold(
body: Container(
child: ElevatedButton(
style: ButtonStyle(),
onPressed: () {
createUserModalBottomSheet(context);
},
child: Text("Start"),
)
)
);
}
}
void createUserModalBottomSheet(context){
showModalBottomSheet(context: context, builder: (BuildContext bc) {
return Container(
child: Switch(value: true, onChanged: (value) => {value = !value}, activeColor:
Colors.grey)
);
}
}
The Problem is that the switch won't change his value. The Modalbottomsheet appears but won't update changes/states.
Does anyone know a solution?
Use StatefulBuilder to update UI inside showModalBottomSheet. Second issue is you need to use a bool variable to hold value.
class StartPageState extends State<Start_Page> {
bool switchValue = false;
///......
void createUserModalBottomSheet(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return StatefulBuilder(
builder: (context, setStateSB) => Container(
child: Switch(
value: switchValue,
onChanged: (value) {
setState(() {
// update parent UI
switchValue = value;
});
setStateSB(() {
// update inner dialog
switchValue = value;
});
},
activeColor: Colors.grey),
),
);
});
}
#override
Widget build(BuildContext context) {
.........

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

Calling initState of the Selected Index's Widget on BottomNavigationBar

I want to call initState of each NavigationBarItem when selectedIndex of the BottomNavigationBar changes. How can I implement it? Right now, their initState are only launched when the main page which contains the BottomNavigationBar is initialized. Can I add something on the onTap function to do this? If I can, what would it be?
import 'package:flutter/material.dart';
main()=>runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_HomeState createState() => new _HomeState();
}
class _HomeState extends State<MyApp> {
int _selectedPage = 0;
final _pageOptions = [
new Page1(),
new Page2(),
new Page3(),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: _pageOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedPage,
onTap: (int index) {
setState(() {
_selectedPage = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Padding(
padding: const EdgeInsets.only(top: 5),
child: Text(
"Page 1",
style: TextStyle(
color: Color(0xFF282858),
fontFamily: "Poppins-Medium",
height: 1),
),
)),
BottomNavigationBarItem(
icon: Icon(
Icons.location_on,
color: Color(0xFF282858),
),
title: Text(
"Page 2",
style: TextStyle(
color: Color(0xFF282858),
height: 1),
)),
BottomNavigationBarItem(
icon: Icon(
Icons.supervised_user_circle,
color: Color(0xFF282858),
),
title: Text(
"Page 3",
style: TextStyle(
color: Color(0xFF282858),
height: 1),
)),
],
),
),
);
}
}
class Page1 extends StatefulWidget {
#override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
#override
void initState() {
// TODO: implement initState
print('This is page 1');
}
#override
Widget build(BuildContext context) {
return Container();
}
}
class Page2 extends StatefulWidget {
#override
_Page2State createState() => _Page2State();
}
class _Page2State extends State<Page2> {
#override
void initState() {
// TODO: implement initState
print('This is page 2');
}
#override
Widget build(BuildContext context) {
return Container();
}
}
class Page3 extends StatefulWidget {
#override
_Page3State createState() => _Page3State();
}
class _Page3State extends State<Page3> {
#override
void initState() {
// TODO: implement initState
print('This is page 3');
}
#override
Widget build(BuildContext context) {
return Container();
}
}
check out this code

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