For screen a Column with a row of two Containers, each showing an image, and an undo FlatButton beneath the row, when I use the InteractiveViewer for the second one in the row it works perfectly but for the first when it is expanded it has the Undo button on top of the image.
Would like for when either of the Containers are used with the Interactive Viewer that they be at the top of the view.
Would also like for Interactive Viewer containers to go full screen on a long press and exit full screen when long press is released.
final TransformationController _transformationController =
TransformationController();
Animation<Matrix4> _animationReset;
AnimationController _controllerReset;
void _onAnimateReset() {
_transformationController.value = _animationReset.value;
if (!_controllerReset.isAnimating) {
_animationReset?.removeListener(_onAnimateReset);
_animationReset = null;
_controllerReset.reset();
}
}
void _animateResetInitialize() {
_controllerReset.reset();
_animationReset = Matrix4Tween(
begin: _transformationController.value,
end: Matrix4.identity(),
).animate(_controllerReset);
_animationReset.addListener(_onAnimateReset);
_controllerReset.forward();
}
void _animateResetStop() {
_controllerReset.stop();
_animationReset?.removeListener(_onAnimateReset);
_animationReset = null;
_controllerReset.reset();
}
void _onInteractionStart(ScaleStartDetails details) {
// If the user tries to cause a transformation while the reset animation is
// running, cancel the reset animation.
if (_controllerReset.status == AnimationStatus.forward) {
_animateResetStop();
}
}
void _onInteractionEnd(ScaleEndDetails details) {
_animateResetInitialize();
}
final TransformationController _transformationController2 =
TransformationController();
Animation<Matrix4> _animationReset2;
AnimationController _controllerReset2;
void _onAnimateReset2() {
_transformationController2.value = _animationReset2.value;
if (!_controllerReset2.isAnimating) {
_animationReset2?.removeListener(_onAnimateReset2);
_animationReset2 = null;
_controllerReset2.reset();
}
}
void _animateResetInitialize2() {
_controllerReset2.reset();
_animationReset2 = Matrix4Tween(
begin: _transformationController2.value,
end: Matrix4.identity(),
).animate(_controllerReset2);
_animationReset2.addListener(_onAnimateReset2);
_controllerReset2.forward();
}
void _animateResetStop2() {
_controllerReset2.stop();
_animationReset2?.removeListener(_onAnimateReset2);
_animationReset2 = null;
_controllerReset2.reset();
}
void _onInteractionStart2(ScaleStartDetails details) {
// If the user tries to cause a transformation while the reset animation is
// running, cancel the reset animation.
if (_controllerReset2.status == AnimationStatus.forward) {
_animateResetStop2();
}
}
void _onInteractionEnd2(ScaleEndDetails details) {
_animateResetInitialize2();
}
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: BGColor,
appBar: AppBar(
backgroundColor: PrimaryColor,
title: Text('App\'name',
style: TextStyle(
color: Colors.white
),
),
leading: GestureDetector(
onTap: () {
Navigator.of(context).pop();},
child: Padding(
padding: EdgeInsets.only(top: 19.0, left: 12.0),
child: Text('Quit',
style: TextStyle(
color: Colors.white54,
fontSize: 18.0,
),
),
),
),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Flexible(
flex: 1,
child: tapInstruct(),
),
SizedBox(height: 5.0),
Flexible(
flex: 9,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 1,
child: InteractiveViewer(
boundaryMargin: EdgeInsets.all(double.infinity),
transformationController: _transformationController,
minScale: 1.0,
maxScale: 5,
onInteractionStart: _onInteractionStart,
onInteractionEnd: _onInteractionEnd,
panEnabled: false,
child: Container(
//margin: EdgeInsets.all(10.0),
child: GestureDetector(
onTap: () => _leftCardTapped(),
child: Padding(
padding: const EdgeInsets.all(2.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(15.0),
child: Image.file(
favpicz[lcn]
// fit: BoxFit.fitHeight,
),
),
),
// ),
),
),
),
),
Divider(
thickness: 2.0,
),
Expanded(
flex: 1,
child: InteractiveViewer(
boundaryMargin: EdgeInsets.all(double.infinity),
transformationController: _transformationController2,
minScale: 1.0,
maxScale: 5,
onInteractionStart: _onInteractionStart2,
onInteractionEnd: _onInteractionEnd2,
panEnabled: false,
child: Container(
//margin: EdgeInsets.all(10.0),
child: GestureDetector(
onTap: () => _rightCardTapped(),
//onLongPress: //TODO full screen image,
// onLongPressEnd: ,//TODO exit full screen
//child: Card(
child: Padding(
padding: const EdgeInsets.all(2.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(15.0),
child: Image.file(
favpicz[rcn],
),
),
),
//),
),
),
),
),
],
),
),
SizedBox(height: 5.0),
Flexible(
flex: 1,
child:
GestureDetector(
child: FlatButton(
onPressed: () => undo(),
child: Text('Undo',
style: TextStyle(
color: BGColor
),),
color: undoButtonColor,
),
),
)
],
),
),
);
}
I was able to get this to look the way I wanted by wrapping the two Containers and the FlatButton in Visibility widgets and creating functions to set the visibility of the other container and flat button to false when the InteractiveViewer animation began and true when it ended.
Using the Visibility widget also worked for the desired full screen effect on long hold.
Related
my video call output
when logged another person to this video call then shows like this.But I want when someone logged to video call then my video should be small.
like this.
and also when click my video then my video should be big and another person's video should be small. how to do that ? I couldn't find the any documentation how to do that
Code video call ui
// video view
Widget _viewRows() {
final views = _getRenderViews();
switch (views.length) {
case 1:
return Column(
children: <Widget>[_videoView(views[0])],
);
case 2:
return Column(
children: <Widget>[
_expandedVideoRow([views[0]]),
_expandedVideoRow([views[1]])
],
);
case 3:
return Column(
children: <Widget>[
_expandedVideoRow(views.sublist(0, 2)),
_expandedVideoRow(views.sublist(2, 3))
],
);
case 4:
return Column(
children: <Widget>[
_expandedVideoRow(views.sublist(0, 2)),
_expandedVideoRow(views.sublist(2, 4))
],
);
default:
}
return Container();
}
How customize the video UI like as I mentioned?
error
To create the layout which you want, edit _viewRow(CallNotifier notifier) and _expandedVideoRow(List views) function with following code : -
Widget _viewRows(CallNotifier notifier) : -
case 2:
return Container(
margin: EdgeInsets.only(top: 100, bottom: 100),
child: Stack(
children: [
_expandedVideoRow([views[secondScreen]]),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(right: 10, bottom: 10),
child: GestureDetector(
onTap: () {
tempSwap = firstScreen;
firstScreen = secondScreen;
secondScreen = tempSwap;
setState(() {});
},
child: SizedBox(
height: 200,
width: 100,
child: _expandedVideoRow([views[firstScreen]])),
),
),
),
],
));
Above code contains _expandedVideoRow([views[secondScreen]]), which is just a simple Expandable Container and we are passing the index of the screen as a parameter. In our case, there are 2 screens hence 2 index that is 0 and 1. I have declared three integer variables here, int firstScreen = 0, int secondScreen = 1 and int tempSwap = 0. The second _expandedVideoRow([views[firstScreen]]) is wrapped by GesutreDector, so when the user taps on that screen the indexes of the variable are swapped which results in swapping the screens, SizedBox to reduce its width and height and Align widget to give the desired position to the second screen.
Widget _expandedVideoRow(List views) : -
Widget _expandedVideoRow(List<Widget> views) {
final wrappedViews = views.map<Widget>(_videoView).toList();
return Row(
children: wrappedViews,
);
}
Remove the Expanded widget that wraps the row like the above code because we can't use the Expanded under the Stack widget.
If you wish to change the bottom icons, then change _toolbar(CallNotifier notifier) function according to your need.
Widget _toolbar(CallNotifier notifier) {
return Container(
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RawMaterialButton(
onPressed: () {
_onToggleMute(notifier);
setState(() {
isMute = !isMute;
});
},
child: Icon(
isMute ? Icons.mic_off : Icons.mic,
color: isMute ? Colors.white : Colors.teal,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: isMute ? Colors.teal : Colors.white,
padding: const EdgeInsets.all(12.0),
),
RawMaterialButton(
onPressed: () => _onCallEnd(context),
child: Icon(
Icons.call_end,
color: Colors.white,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: Colors.redAccent,
padding: const EdgeInsets.all(15.0),
),
],
),
);
}
Code which I use in my app, full code : -
class _CallScreenState extends State<CallScreen> {
double globalHeight;
int firstScreen = 0;
int secondScreen = 1;
int tempSwap = 0;
bool isMute = false;
void initState() {
super.initState();
}
List<Widget> _getRenderViews(CallNotifier model) {
final List<StatefulWidget> list = [];
list.add(RtcLocalView.SurfaceView());
model.users
.forEach((int uid) => list.add(RtcRemoteView.SurfaceView(uid: uid)));
return list;
}
Widget _videoView(view) {
return Expanded(child: Container(child: view));
}
Widget _expandedVideoRow(List<Widget> views) {
final wrappedViews = views.map<Widget>(_videoView).toList();
return Row(
children: wrappedViews,
);
}
Widget _viewRows(CallNotifier notifier) {
final views = _getRenderViews(notifier);
switch (views.length) {
case 1:
return Container(
margin: EdgeInsets.only(top: 100, bottom: 100),
child: Column(
children: <Widget>[_videoView(views[0])],
));
case 2:
return Container(
margin: EdgeInsets.only(top: 100, bottom: 100),
child: Stack(
children: [
_expandedVideoRow([views[secondScreen]]),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(right: 10, bottom: 10),
child: GestureDetector(
onTap: () {
tempSwap = firstScreen;
firstScreen = secondScreen;
secondScreen = tempSwap;
setState(() {});
},
child: SizedBox(
height: 200,
width: 100,
child: _expandedVideoRow([views[firstScreen]])),
),
),
),
],
));
default:
}
return Container();
}
Widget _toolbar(CallNotifier notifier) {
return Container(
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RawMaterialButton(
onPressed: () {
_onToggleMute(notifier);
setState(() {
isMute = !isMute;
});
},
child: Icon(
isMute ? Icons.mic_off : Icons.mic,
color: isMute ? Colors.white : Colors.teal,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: isMute ? Colors.teal : Colors.white,
padding: const EdgeInsets.all(12.0),
),
RawMaterialButton(
onPressed: () => _onCallEnd(context),
child: Icon(
Icons.call_end,
color: Colors.white,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: Colors.redAccent,
padding: const EdgeInsets.all(15.0),
),
],
),
);
}
void _onCallEnd(BuildContext context) {
Navigator.pop(context);
}
void _onToggleMute(CallNotifier notifier) {
notifier.isMuted = notifier.isMuted;
notifier.engine.muteLocalAudioStream(notifier.isMuted);
}
#override
Widget build(BuildContext context) {
return BaseWidget<CallNotifier>(
model: CallNotifier(),
onModelReady: (model) => model.init(widget.channelName, widget.token),
builder: (context, notifier, child) {
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: Stack(
children: <Widget>[
_viewRows(notifier),
Align(
alignment: Alignment.bottomCenter,
child: _toolbar(notifier)),
],
),
),
);
});
}
}
I am designing a screen with two main sections:
A carousel slider with a TextField below it
A button
I want the button to be at the bottom of the screen.
To achieve this, I have wrapped the column for 1. in a Flexible widget and set the mainAxisSize for the Column to max, and the mainAxis size for the Column containing the button to min.
Now when I click on the TextField, the keyboard appears, and I receive a Bottom Overflowed error with the Button appearing on top of the TextField.
How do I ensure that the Button stays at the bottom of the screen when the keyboard appears? I have tried wrapping both the Columns in another Column which in turn had been wrapped by a SingleChildScrollView widget, but that overrides the MainAxisSize.max property apparently, and renders the lower Column (containing the Button) just below the upper Column as seen below.
My Code:
class SigninScreen extends StatefulWidget {
const SigninScreen({super.key});
#override
State<SigninScreen> createState() => _SigninScreenState();
}
class _SigninScreenState extends State<SigninScreen> {
int _currentCarouselIndex = 0;
List<Widget> indicators(imagesLength, currentIndex) {
return List<Widget>.generate(imagesLength, (index) {
return Container(
margin: EdgeInsets.symmetric(
vertical: 1.h,
horizontal: 2,
),
width: currentIndex == index ? 15 : 10,
height: 3,
decoration: BoxDecoration(
color: currentIndex == index ? primary : grey,
borderRadius: const BorderRadius.all(
Radius.circular(2),
),
),
);
});
}
TextEditingController phoneController = TextEditingController();
#override
void dispose() {
phoneController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: GestureDetector(
onTap: (){ FocusManager.instance.primaryFocus?.unfocus();},
behavior: HitTestBehavior.opaque,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
fit: FlexFit.loose,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
CarouselSlider(
items: carouselImageList.map<Widget>((i){
return Builder(
builder: (context){
return Container(
width: 100.w,
height: 83.w,
decoration: BoxDecoration(
image: DecorationImage(image: AssetImage(i), fit: BoxFit.fill),
),
);
}
);
}).toList(),
options: CarouselOptions(
height: 83.w,
aspectRatio: 1/0.83,
autoPlay: true,
autoPlayInterval: const Duration(seconds: 3),
initialPage: 0,
viewportFraction: 1,
onPageChanged: (index, timed) {
setState(() {
_currentCarouselIndex = index;
});
}
),
),
SizedBox(height: 1.h,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: indicators(
carouselImageList.length, _currentCarouselIndex),
),
SizedBox(height: 2.h,),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(isEnglish ? "Enter your phone number" : "अपना फ़ोन नंबर दर्ज करें", style: globalTextStyle.copyWith(fontSize: 5.w, fontWeight: FontWeight.bold),),
],
),
SizedBox(height: 1.h),
PhoneNumberField(phoneController: phoneController),
SizedBox(height: 1.h),
Text(isEnglish ? "OTP will be sent on this number." : "इस नंबर पर ओटीपी भेजा जाएगा।", style: globalTextStyle.copyWith(fontSize: 3.w,),),
],
),
),
],
),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(width: 90.w, height: 15.w, color: primary, onTap: (){
// Navigator.pushNamed(context, otp);
(phoneController.text.length == 10) ?
Navigator.push(context, MaterialPageRoute(builder: (context) =>
OTPScreen(phoneNumber: "+91${phoneController.text}")))
: ShowSnackbar.showSnackBar(context, isEnglish ? "Enter a valid 10 digit phone number." : "एक मान्य 10 अंकों का फ़ोन नंबर दर्ज करें।");
}, text: isEnglish ? "Get OTP" : "ओटीपी प्राप्त करें", fontColor: white, borderColor: primary,),
SizedBox(height: 1.h,),
Text(isEnglish ? "By signing up, you agree to our Terms and Services" : "साइन अप करके, आप हमारी शर्तों से सहमत होते हैं और सेवाएं", style: globalTextStyle.copyWith(fontSize: 2.5.w,),),
SizedBox(height: 2.h,),
],
),
],
),
),
),
),
);
}
}
How do I get the Button to stay at the bottom of the screen even when the keyboard appears?
Remove the Flexible and try the below code.
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: GestureDetector(
onTap: () {
FocusManager.instance.primaryFocus?.unfocus();
},
behavior: HitTestBehavior.opaque,
child: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
CarouselSlider(
items: carouselImageList.map<Widget>((i) {
return Builder(builder: (context) {
return Container(
width: 100.w,
height: 100.w,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(i), fit: BoxFit.fill),
),
);
});
}).toList(),
options: CarouselOptions(
height: 83.w,
aspectRatio: 1 / 0.83,
autoPlay: true,
autoPlayInterval: const Duration(seconds: 3),
initialPage: 0,
viewportFraction: 1,
onPageChanged: (index, timed) {
setState(() {
_currentCarouselIndex = index;
});
}),
),
SizedBox(
height: 1.h,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: indicators(
carouselImageList.length, _currentCarouselIndex),
),
SizedBox(
height: 2.h,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
"Enter your phone number",
style: TextStyle(
fontSize: 5.w, fontWeight: FontWeight.bold),
),
],
),
SizedBox(height: 1.h),
TextFormField(
controller: phoneController,
keyboardType: TextInputType.phone,
),
SizedBox(height: 1.h),
Text(
"OTP will be sent on this number.",
style: TextStyle(
fontSize: 3.w,
),
),
],
),
),
Expanded(
child: Container(),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
width: 90.w,
height: 15.w,
color: primary!,
onTap: () {},
text: "Get OTP",
fontColor: white!,
borderColor: primary!,
),
SizedBox(
height: 1.h,
),
Text(
"By signing up, you agree to our Terms and Services",
style: TextStyle(
fontSize: 2.5.w,
),
),
SizedBox(
height: 2.h,
),
],
),
],
),
),
),
),
),
);
}
In your Scaffold set resizeToAvoidBottomInset: property to false I think this will help. Otherwise I observed that the render overflow error is from the column that contains the PhoneNumberField so try Increasing the height of the SizedBox after the PhoneNumberField if the first method doesn't work.
I am building an app that has gmail type animation in which when the circular avatar button is clicked the List tile gets selected with a rotation animation.
The list of items are built using ListView.builder. But when the circle avatar of a list item is clicked it affects the whole list of items and rotates every circle avatar. From similar questions asked I changed Listtile into a separate stateful widget as every item could hold its own state. But this doesn't change the output.
I didn't get the output even after using keys(Unique and object). I am stuck here for a long time but couldn't figure what and how to do it. Is there any way to differentiate widgets in Listview.builder and maintain their states seperately?
Alternatively when I use checkbox/selectable icon its holding the state for each item induvidually.
class MailList extends StatefulWidget {
int index;
List<MailModel> account;
Animation rotationAnim;
AnimationController circleAvatarController;
OneShotAnimation riveAnimationController1;
OneShotAnimation riveAnimationController2;
List<int> selectedIndexes = [];
UniqueKey key;
MailList({
required this.index,
required this.account,
required this.rotationAnim,
required this.circleAvatarController,
required this.riveAnimationController1,
required this.riveAnimationController2,
required this.selectedIndexes,
required this.key
}):super(key: key);
#override
_MailListState createState() => _MailListState();
}
class _MailListState extends State<MailList> {
#override
Widget build(BuildContext context) {
return Listener(
key: UniqueKey(),
child: Dismissible(
key: ValueKey(widget.index),
child: GestureDetector(
key: ValueKey(widget.index),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
shape: BoxShape.rectangle,
color: widget.account[widget.index].isSelected ? Colors.blue.withOpacity(0.2): null
),
height: 70,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
GestureDetector(
child: AnimatedBuilder(
builder: (context, child){
return Transform(
alignment: Alignment.center,
child: CircleAvatar(
key: ValueKey(widget.index),
backgroundColor: widget.account[widget.index].isSelected ? widget.account[widget.index].defaultOnSelectedColor : widget.account[widget.index].senderInfo.profileColor.withOpacity(0.75),
child: TweenAnimationBuilder(
tween: Tween<double>(begin: 0, end: 1),
builder: (context, double anim, child){
return widget.rotationAnim.value > 90 && widget.account[widget.index].isSelected
? Opacity(
child: Icon(
widget.account[widget.index].defaultOnSelectedIcon,
color: Colors.white,
),
opacity: anim,
)
: Text(
widget.account[widget.index].senderInfo.senderName[0].toString(),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 21.0,
color: Colors.white
));
},
duration: const Duration(seconds: 1),
),
),
transform: /*isSelected(widget.index) ? (Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY((widget.rotationAnim.value) / 180 * math.pi))
: (Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(0 / 180 * math.pi))*/
Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY((widget.rotationAnim.value) / 180 * math.pi),
);
},
animation: widget.circleAvatarController,
),
onTap: (){
selectUnSelect(widget.index);
widget.account[widget.index].isSelected = !widget.account[widget.index].isSelected;
widget.account[widget.index].isSelected ? widget.circleAvatarController.forward() : widget.circleAvatarController.reverse();
setState(() {
});
},
)
],
mainAxisAlignment: MainAxisAlignment.start,
),
const SizedBox(
width: 10.0,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.account[widget.index].senderInfo.senderName,
style: TextStyle(
fontSize: 16.0,
fontWeight: widget.account[widget.index].isSeen
? FontWeight.normal
: FontWeight.bold
),
),
Text(
"10:00"
)
],
)
),
const SizedBox(
height: 5.0,
),
Text(
widget.account[widget.index].title,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.5,
fontWeight: widget.account[widget.index].isSeen
? FontWeight.normal
: FontWeight.bold
),
),
const SizedBox(
height: 5.0,
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: Text(
widget.account[widget.index].content,
overflow: TextOverflow.ellipsis,
),
),
GestureDetector(
child: Container(
child: widget.account[widget.index].starred ? Icon(
Icons.star,
color: Colors.orange[300],
):
Icon(
Icons.star_border
),
),
onTap: (){
setState(() {
widget.account[widget.index].starred = !widget.account[widget.index].starred;
});
},
)
],
),
),
const SizedBox(
height: 5.0,
),
],
),
)
],
),
),
),
onTap: (){
},
),
background: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: dragWidget(
left: 8.0,
controller: widget.riveAnimationController1
)),
],
),
secondaryBackground: Container(
alignment: Alignment.centerRight,
color: Colors.green,
child: Padding(
padding: EdgeInsets.only(
right: 8.0
),
child: SizedBox(
height: 50.0,
width: 50.0,
child: RiveAnimation.asset(
"assets/gmail_drag_anim.riv",
controllers: [
widget.riveAnimationController2
],
animations: [
"Animation 1"
],
fit: BoxFit.fill,
//antialiasing: false,
),
),
),
),
onDismissed: (direction){
widget.account.removeAt(widget.index);
},
),
);
}
bool isSelected(int index){
return widget.selectedIndexes.contains(index);
}
selectUnSelect(int index){
if(isSelected(index)){
widget.circleAvatarController.reverse();
Future.delayed(Duration(milliseconds: 320), (){
widget.selectedIndexes.remove(index);
setState(() {
});
});
}else{
widget.selectedIndexes.add(index);
}
}
Widget dragWidget({
double? left,
double? right,
required OneShotAnimation controller
}){
return Container(
alignment: right != null ? Alignment.centerRight : Alignment.centerLeft,
color: Colors.green,
child: Padding(
padding: EdgeInsets.only(
left: left != null ? left : 0.0,
right: right != null ? right: 0.0
),
child: SizedBox(
height: 50.0,
width: 50.0,
child: RiveAnimation.asset(
"assets/gmail_drag_anim.riv",
controllers: [
controller
],
animations: [
"Animation 1"
],
fit: BoxFit.fill,
//antialiasing: false,
),
),
),
);
}
} ```
// ListView.builder:
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index){
return MailList(
key: GlobalKey<_MailListState>(),
index: index,
account: account1_mail_data,
rotationAnim: rotationAnim,
circleAvatarController: circleAvatarController,
riveAnimationController1: _riveAnimationController1,
riveAnimationController2: _riveAnimationController2,
selectedIndexes: selectedIndexes
);
},
childCount: account1_mail_data.length
),
)
I'm making a Flutter Web App and I have something like this
class _HomeViewState extends State<HomeView> {
#override
Widget build(BuildContext context) {
var posts = Provider.of<List<Post>>(context);
List<int> l =[1,2,3,4];
return Scaffold(
backgroundColor: Colors.white,
appBar: TheAppBaar,
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(child: Padding(
padding: EdgeInsets.all(20.0),
child: Column(
children: [
CreatePost(connUserUid: widget.user.uid, connUserImage:
userFetched.get('profileImage').toString(),
connUserFullname: userFetched.get('fullName').toString(),),
SizedBox(height: 10,),
ListView.separated(itemBuilder: (BuildContext context, int
index) {
return Container(
height: 150,
child: Text('${l[index]}'),
);
},
separatorBuilder:
(BuildContext context, int index) => Divider(),
itemCount: l.length)
],
),
),
),
),
);
}
}
Every time I want to display a ListView with those posts fetched or anything, the method Create Post and that Sized Box disappear and the list is not displayed either.
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: Container(
width: 400,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(10)),
boxShadow: [BoxShadow(
color:kPrimaryColor.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 3,
offset: Offset(0,3)
)]
),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Align(
alignment: Alignment.center,
child: Container(
height: 50,
child: Image.network(widget.connUserImage),
),
),
Align(
alignment: Alignment.center,
child: Container(width: 30,child: Divider(
color: kPrimaryColor,
thickness: 2,
),),
),
SizedBox(height: 8,),
TextField(
maxLines: 5,
decoration: InputDecoration(
errorText: errorTextMessage,
border: OutlineInputBorder(
borderSide: BorderSide(color: kPrimaryColor)
),
labelText: 'Yadda, Yadda, Yadda...'
),
onChanged: (String value) {
setState(() {
postDescription = value;
errorTextMessage = null;
spinner = false;
});
},
),
SizedBox(height: 12,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
IconButton(
icon: Icon(Icons.add_a_photo_rounded, color: Color
(0xFFE53E00),
size:
25,),
onPressed: () {
imagePicker();
},
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: imageInfo == null ? Container() : Text(imageInfo.fileName),
)
],
),
Icon(Icons.pin_drop_rounded, color: Color(0xFF0077e5),
size:
25,),
Icon(Icons.map_rounded, color: Color(0xFF26c118),
size: 25,),
spinner? Padding(
padding: EdgeInsets.all(15.0),
child: CircularProgressIndicator(backgroundColor: kPrimaryColor,),
) :
IconButton(icon:
Icon(Icons
.arrow_forward_ios_outlined,
color:
kPrimaryColor,size: 25,), onPressed: () {
},),
],
)
],
),
),
),
);
}
This is the CreatePost method, just a " card ",
If I make a ListView all disappear, but if in my Column from the first code, I add 1 PostView by 1, they are displayed. I don't understand what's wrong. Should I remove those Align or I don't know. In my console I do not have any errors.
Thank you in advance!
how to execute the API when the textfield is filled. so when the textfield is filled it starts executing the API.
I've tried making a check function that works if the textfield is not null then I can only start the API execution. in the following ways:
in the block I make a check function to check whether the textfield (_prefix) is filled or not but this function is an error when called in inStState in the UI section.
BLOC :
class DenomPulsaBloc {
final _repository = EresidenceRepository();
final _prefix = BehaviorSubject<String>();
SharedPreferences sPrefs;
final BehaviorSubject<List<Payload>> _subject = BehaviorSubject<List<Payload>>();
Function(String) get prefix => _prefix.sink.add;
cek() async{
if(_prefix.value.length >= 3) {
denomPulsa();
}else{
print("GAGAL");
}
}
denomPulsa() async{
try{
sPrefs = await SharedPreferences.getInstance();
ListPulsaResponses responses = await _repository.listDenomPulsa(sPrefs.getString("userid"), sPrefs.getString("password"), sPrefs.getString("imei"),
sPrefs.getString("coordinate"), sPrefs.getString("bit61"), sPrefs.getString("bit62"), _prefix.value);
List<Payload> list = responses.data.payload;
_subject.sink.add(list);
print(list);
}catch(e){
print(e.toString());
_subject.sink.add(e);
}
}
dispose(){
_subject.close();
_prefix.close();
}
BehaviorSubject<List<Payload>> get subject => _subject;
}
UI :
class _PulsaPageState extends State<PulsaPage> {
DenomPulsaBloc denomPulsaBloc;
int selectedCard = -1;
#override
void initState() {
super.initState();
denomPulsaBloc = DenomPulsaBloc();
denomPulsaBloc.cek();
}
#override
void dispose() {
denomPulsaBloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 2,
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
brightness: Brightness.light,
elevation: 0.0,
leading: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Icon(
Icons.arrow_back_ios,
color: Colors.black,
size: SizeConfig.texIconSize,
),
),
),
body: SafeArea(
child: Container(
height: SizeConfig.screenHeight,
width: SizeConfig.screenWidth,
padding: EdgeInsets.symmetric(horizontal: SizeConfig.widthMultiplier * 4),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
alignment: Alignment.centerLeft,
child: Text(
'Pulsa',
style: AppTheme.styleSubTitleBoldLarge,
)
),
Container(
margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 4),
decoration: BoxDecoration(
boxShadow: <BoxShadow>[
BoxShadow(blurRadius: 5.0, color: Colors.black12)
]
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Container(
padding: EdgeInsets.all(SizeConfig.heightMultiplier * 2),
color: Colors.white,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 1,
child:
Container(
child: Icon(Icons.phone_android, color: Colors.green, size: SizeConfig.texIconSize,),
),
),
Expanded(
flex: 9,
child: Container(
margin: EdgeInsets.only(left: SizeConfig.widthMultiplier * 3),
child:Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Text(
'Number',
style: AppTheme.styleSubTitle,
),
),
Container(
child: Stack(children: <Widget>[
Container(
height: SizeConfig.heightMultiplier * 5.5,
child: TextFormField(
inputFormatters: [
new LengthLimitingTextInputFormatter(13)
],
style: AppTheme.styleSubTitleBlackSmall,
decoration: const InputDecoration(
hintText: '090900909'
),
onChanged: denomPulsaBloc.prefix,
),
),
Align(
alignment: Alignment.centerRight,
child: Container(
alignment: Alignment.centerRight,
height: SizeConfig.texIconSize,
width: SizeConfig.texIconSize,
margin: EdgeInsets.only(right: SizeConfig.widthMultiplier * 9, top: SizeConfig.heightMultiplier * 1.5),
child: Image.asset(
"res/images/xl.png",
fit: BoxFit.fill,
)
),
),
Align(
alignment: Alignment.centerRight,
child: Container(
margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 1.5),
child: InkWell(
onTap: (){},
child: Icon(
Icons.close,
size: SizeConfig.texIconSize,
),
)
)
)
]),
),
]
),
)
),
],
),
),
),
),
Expanded(
child: Container(
width: SizeConfig.screenWidth,
height: SizeConfig.screenHeight,
child: StreamBuilder(
stream: denomPulsaBloc.subject,
builder: (context, AsyncSnapshot<List<Payload>> snapshot) {
if (snapshot.hasData) {
// print(snapshot.data);
return listDenomPulsa(snapshot);
}else{
Error();
}
return Container();
},
),
),
),
],
),
),
),
),
),
);
}