Related
I have a form consisting of nextformfield and elevatedButton. I want to repeat the same form just below on the screen in the same page. I have never tried to do this and it didn't work out, please help.
Here is my code in which I want to do this:
import 'dart:math';
import 'package:flutter/material.dart';
class gradysu extends StatefulWidget {
const gradysu({super.key});
#override
State<gradysu> createState() => _gradysuState();
}
class _gradysuState extends State<gradysu> {
var _formKey = GlobalKey<FormState>();
var x, y, c, d;
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text('Квадрат'),
backgroundColor: Color.fromARGB(255, 252, 252, 252),
automaticallyImplyLeading: true,
),
body: SingleChildScrollView(
child: Container(
height: 800,
width: 639,
/* decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/image/fon2.png"),
fit: BoxFit.cover,
),
),*/
child: Column(
children: [
Container(
color: Colors.brown,
height: 50.0,
width: 1000.0,
child: Center(
child: const Text(
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16, color: Colors.white),
'Расчёт площади квадрата S=:'))),
Row(children: <Widget>[
Container(
padding: const EdgeInsets.all(10.0),
child: const Text('Введите градусы:',
style: TextStyle(color: Color.fromARGB(255, 0, 0, 0))),
),
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: TextFormField(
keyboardType: TextInputType.number,
validator: (value) {
if (value!.isEmpty) return 'Задайте градусы';
try {
x = double.parse(value);
} catch (h) {
x;
return h.toString();
}
}))),
]),
const SizedBox(height: 20.0),
Text(
y == null ? '' : ' $y (рад) ',
style: const TextStyle(
fontSize: 20.0, color: Color.fromARGB(255, 0, 0, 0)),
),
const SizedBox(height: 20.0),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
setState(() {
if (x is double) y = ((x * pi) / 180);
});
}
},
style: ElevatedButton.styleFrom(
foregroundColor: Color.fromARGB(255, 235, 235,
235), //change background color of button
backgroundColor:
Colors.brown, //change text color of button
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
elevation: 5.0,
),
child: const Padding(
padding: EdgeInsets.all(8),
child: Text(
'Вычислить',
style: TextStyle(fontSize: 20),
))),
SizedBox(height: 10.0),
],
),
),
),
),
);
}
}
class gradysu1 extends StatefulWidget {
const gradysu1({super.key});
#override
State<gradysu1> createState() => _gradysu1State();
}
class _gradysu1State extends State<gradysu1> {
var _formKey1 = GlobalKey<FormState>();
var a, c;
#override
Widget build(BuildContext context) {
return Form(
key: _formKey1,
child: Scaffold(
body: SafeArea(
child: Column(children: [
Row(children: <Widget>[
Container(
padding: const EdgeInsets.all(10.0),
child: const Text('Введите градусы:',
style: TextStyle(color: Color.fromARGB(255, 0, 0, 0))),
),
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: TextFormField(
keyboardType: TextInputType.number,
validator: (value) {
if (value!.isEmpty) return 'Задайте градусы';
try {
a = double.parse(value);
} catch (h) {
a;
return h.toString();
}
}))),
]),
const SizedBox(height: 20.0),
Text(
a == null ? '' : ' $a (рад) ',
style: const TextStyle(
fontSize: 20.0, color: Color.fromARGB(255, 0, 0, 0)),
),
const SizedBox(height: 20.0),
ElevatedButton(
onPressed: () {
if (_formKey1.currentState!.validate()) {
setState(() {
if (c is double) a = ((c * pi) / 180);
});
}
},
style: ElevatedButton.styleFrom(
foregroundColor: Color.fromARGB(
255, 235, 235, 235), //change background color of button
backgroundColor: Colors.brown, //change text color of button
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
elevation: 5.0,
),
child: const Padding(
padding: EdgeInsets.all(8),
child: Text(
'Вычислить',
style: TextStyle(fontSize: 20),
))),
]))));
}
}
This is what it looks like
And this is how it should look like
I tried to create another class, but flutter just doesn't see it, I tried to add it to the main code, but then he just didn't count, but threw an exception.
Try to create reusable widget or builder method to minimize the snippet/code-dublication.
import 'dart:math';
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MaterialApp(
home: gradysu(),
));
}
class gradysu extends StatefulWidget {
const gradysu({super.key});
#override
State<gradysu> createState() => _gradysuState();
}
class _gradysuState extends State<gradysu> {
var _formKey = GlobalKey<FormState>();
var x, y, c, d;
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text('Квадрат'),
backgroundColor: Color.fromARGB(255, 252, 252, 252),
automaticallyImplyLeading: true,
),
body: SingleChildScrollView(
child: Container(
height: 800,
width: 639,
/* decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/image/fon2.png"),
fit: BoxFit.cover,
),
),*/
child: Column(
children: [
Container(
color: Colors.brown,
child: Center(
child: const Text(
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16, color: Colors.white),
'Расчёт площади квадрата S=:'))),
Row(children: <Widget>[
Container(
padding: const EdgeInsets.all(10.0),
child: const Text('Введите градусы:',
style: TextStyle(color: Color.fromARGB(255, 0, 0, 0))),
),
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: TextFormField(
keyboardType: TextInputType.number,
validator: (value) {
if (value!.isEmpty) return 'Задайте градусы';
try {
x = double.parse(value);
} catch (h) {
x;
return h.toString();
}
},
),
),
),
]),
const SizedBox(height: 20.0),
Text(
y == null ? '' : ' $y (рад) ',
style: const TextStyle(
fontSize: 20.0, color: Color.fromARGB(255, 0, 0, 0)),
),
const SizedBox(height: 20.0),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
setState(() {
if (x is double) y = ((x * pi) / 180);
});
}
},
style: ElevatedButton.styleFrom(
foregroundColor: Color.fromARGB(
255, 235, 235, 235), //change background color of button
backgroundColor: Colors.brown, //change text color of button
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
elevation: 5.0,
),
child: const Padding(
padding: EdgeInsets.all(8),
child: Text(
'Вычислить',
style: TextStyle(fontSize: 20),
),
),
),
SizedBox(height: 10.0),
/// second item , you can create a build method for this
Row(children: <Widget>[
Container(
padding: const EdgeInsets.all(10.0),
child: const Text('Введите градусы:',
style: TextStyle(color: Color.fromARGB(255, 0, 0, 0))),
),
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: TextFormField(
keyboardType: TextInputType.number,
validator: (value) {
if (value!.isEmpty) return 'Задайте градусы';
try {
x = double.parse(value);
} catch (h) {
x;
return h.toString();
}
},
),
),
),
]),
const SizedBox(height: 20.0),
Text(
y == null ? '' : ' $y (рад) ',
style: const TextStyle(
fontSize: 20.0, color: Color.fromARGB(255, 0, 0, 0)),
),
const SizedBox(height: 20.0),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
setState(() {
if (x is double) y = ((x * pi) / 180);
});
}
},
style: ElevatedButton.styleFrom(
foregroundColor: Color.fromARGB(
255, 235, 235, 235), //change background color of button
backgroundColor: Colors.brown, //change text color of button
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
elevation: 5.0,
),
child: const Padding(
padding: EdgeInsets.all(8),
child: Text(
'Вычислить',
style: TextStyle(fontSize: 20),
),
),
),
SizedBox(height: 10.0),
],
),
),
),
),
);
}
}
This is a part of a project regarding feedback forms.
I already manage to create the validation form properly thanks to the answers here in StackOverflow developers.
But the problem is creating this regular expression which seems to not work on the validation form. I found in flutter docs the Iterable but I do not know how can I implement it on the dart page.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class SimpleDialog extends StatelessWidget {
// ignore: prefer_typing_uninitialized_variables
final title;
const SimpleDialog(this.title, {super.key});
#override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Alert'),
content: Text(title),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'))
],
);
}
}
class FeedbackPage extends StatefulWidget {
const FeedbackPage({super.key});
#override
State<FeedbackPage> createState() => _FeedbackPageState();
}
class _FeedbackPageState extends State<FeedbackPage> {
final nameOfuser = TextEditingController();
final emailOfuser = TextEditingController();
final messageOfuser = TextEditingController();
final _formKey = GlobalKey<FormState>();
List<bool> isTypeSelected = [false, false, false, true, true];
#override
Widget build(BuildContext context) {
return Scaffold(
// AppBar para sa taas ng design
appBar: AppBar(
centerTitle: true,
title: const Text(
"PicLeaf",
style: TextStyle(
color: Color.fromRGBO(102, 204, 102, 1.0),
fontWeight: FontWeight.bold),
),
backgroundColor: Colors.white,
shadowColor: const Color.fromARGB(255, 95, 94, 94),
),
//body of the application
backgroundColor: const Color(0xffeeeeee),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const SizedBox(
height: 20,
),
const Text(
"Feedback",
style: TextStyle(
fontSize: 30.0,
fontFamily: 'RobotoBold',
fontWeight: FontWeight.bold,
color: Color.fromRGBO(102, 204, 102, 1.0)),
),
const Text(
"Give us your feedback!",
style: TextStyle(
fontSize: 18.0,
fontFamily: 'RobotoMedium',
),
),
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
const SizedBox(height: 16.0),
TextFormField(
controller: nameOfuser,
decoration: const InputDecoration(
filled: true,
fillColor: Colors.white,
hintText: "Name",
border: OutlineInputBorder(),
),
validator: (nameOfuser) {
if (nameOfuser == null || nameOfuser.isEmpty) {
return 'Please enter your Name';
}
return null;
},
),
const SizedBox(height: 8.0),
TextFormField(
controller: emailOfuser,
decoration: const InputDecoration(
filled: true,
fillColor: Colors.white,
hintText: "Email",
border: OutlineInputBorder(),
),
validator: (emailOfuser) {
if (emailOfuser == null || emailOfuser.isEmpty) {
String emailOfuser1 = emailOfuser.toString();
String pattern = r'\w+#\w+\.\w+';
if (RegExp(pattern).hasMatch(emailOfuser1)) {
return 'Please enter your Email Properly';
} else {
return 'Please enter your Email';
}
}
return null;
},
),
const SizedBox(height: 8.0),
TextFormField(
controller: messageOfuser,
maxLines: 6,
decoration: const InputDecoration(
filled: true,
fillColor: Colors.white,
hintText: "Message",
border: OutlineInputBorder(),
),
validator: (messageOfuser) {
if (messageOfuser == null || messageOfuser.isEmpty) {
return 'Please enter your Message';
}
return null;
},
),
const SizedBox(height: 8.0),
MaterialButton(
height: 50.0,
minWidth: double.infinity,
color: const Color.fromRGBO(102, 204, 102, 1.0),
onPressed: () {
if (_formKey.currentState!.validate()) {
showDialog(
context: context,
builder: (BuildContext context) {
return const SimpleDialog(
'Feedback Submitted');
});
Map<String, dynamic> data = {
"Name": nameOfuser.text,
"Email": emailOfuser.text,
"Message": messageOfuser.text,
"Time": FieldValue.serverTimestamp(),
};
setState(() {
nameOfuser.clear();
emailOfuser.clear();
messageOfuser.clear();
});
FirebaseFirestore.instance
.collection("FeedbackMessages")
.add(data);
}
},
child: const Text(
"SUBMIT",
style: TextStyle(
fontFamily: 'RobotoBold',
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
const SizedBox(
height: 10,
),
Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 10),
child: const Text(
'Contact Us!',
style: TextStyle(
fontSize: 30,
fontFamily: 'RobotoBold',
color: Color.fromRGBO(102, 204, 102, 1.0)),
textAlign: TextAlign.center,
),
),
Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
margin: const EdgeInsets.symmetric(horizontal: 0),
child: TextButton.icon(
// <-- TextButton
onPressed: () {},
icon: const Icon(
Icons.facebook,
color: Colors.black,
size: 35.0,
),
label: const Text(
'facebook.com/picleaf',
style: TextStyle(fontFamily: 'RobotoMedium'),
),
style: TextButton.styleFrom(
foregroundColor: Colors.black,
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 10),
margin: const EdgeInsets.symmetric(horizontal: 0),
child: TextButton.icon(
// <-- TextButton
onPressed: () {},
icon: const Icon(
Icons.email,
color: Colors.black,
size: 35.0,
),
label: const Text(
'picleaf#gmail.com',
style: TextStyle(fontFamily: 'RobotoMedium'),
),
style: TextButton.styleFrom(
foregroundColor: Colors.black,
),
)),
],
)
],
),
),
),
],
),
));
}
}
You can use this validator:
validator: (emailOfuser) {
if (emailOfuser == null || emailOfuser.isEmpty) {
return 'Please enter your Email';
} else if (emailOfuser.isNotEmpty) {
String emailOfuser1 = emailOfuser.toString();
String pattern = r'\w+#\w+\.\w+';
if (RegExp(pattern).hasMatch(emailOfuser1) == false) {
return 'Please enter your Email Properly';
}
}
return null;
},
try to implement like this:
import 'package:flutter/material.dart';
class EmailValidationForm extends StatefulWidget {
const EmailValidationForm({Key? key}) : super(key: key);
#override
State<EmailValidationForm> createState() => _EmailValidationFormState();
}
class _EmailValidationFormState extends State<EmailValidationForm> {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Email Validation'),
),
body: Form(
key: formKey,
child: Column(
children: [
TextFormField(
autofocus: false,
maxLength: 300,
keyboardType: TextInputType.emailAddress,
autocorrect: false,
validator: (email) {
if (email!.isEmpty) {
return 'required field';
} else if (email.trim().contains(' ')) {
return 'contain space';
} else if (!emailValid(email)) {
return 'invalid email';
}
return null;
},
onSaved: (email) {
return debugPrint(email);
},
),
ElevatedButton(
onPressed: () {
if (formKey.currentState!.validate()) {
formKey.currentState!.save();
}
},
child: const Text('Validate')),
],
),
),
);
}
}
bool emailValid(String email) {
final RegExp regex = RegExp(
r"^(([^<>()[\]\\.,;:\s#\']+(\.[^<>()[\]\\.,;:\s#\']+)*)|(\'.+\'))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$");
return regex.hasMatch(email.trim());
}
Hey guys I'm working on some project and need to create a custom dropdown,
like this
I am able to make that here is the code, the code is messy but I will refactor it once I make it work. Or if you have some other way how I can accomplish this I'm open to suggestions.
GlobalKey? actionKey = GlobalKey();
List<String> picked = [];
List<IconData> icons = [
Icons.blur_circular_outlined,
Icons.sports_basketball,
Icons.sports_baseball_sharp,
Icons.sports_tennis_rounded,
Icons.people,
];
List<String> sports = [
"Fudbal",
"Kosarka",
"Tenis",
"Stoni tenis",
"Kuglanje"
];
List<int> ints = [0, 1, 2, 3, 4];
List<bool> booles = [false, false, false, false, false];
OverlayEntry? overlayEntry;
var position;
double? y;
double? x;
void findDropdownData() {
RenderBox renderBox =
actionKey!.currentContext!.findRenderObject() as RenderBox;
position = renderBox.localToGlobal(Offset.zero);
y = position!.dy;
x = position!.dx;
}
OverlayEntry _overlayEntryBuilder() {
return OverlayEntry(
builder: (context) {
return Positioned(
// top: position,
left: 16.w,
right: 16.w,
child: Material(
child: dropdownExpanded(),
),
);
},
);
}
Widget buildRows(i) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 17.w, vertical: 15.h),
child: Row(
children: [
SizedBox(
height: 24.h,
width: 24.w,
child: Checkbox(
activeColor: style.purpleMain,
value: booles[i],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4)),
onChanged: (value) {
setState(() {
booles[i] = value!;
booles[i] == true
? picked.add(sports[i])
: picked.remove(sports[i]);
});
},
),
),
SizedBox(
width: 10.w,
),
Text(
sports[i],
style: TextStyle(
color: booles[i] == true ? style.purpleMain : Colors.grey,
),
),
const Spacer(),
Icon(
icons[i],
color: booles[i] == true ? style.purpleMain : Colors.grey,
size: 15,
),
],
),
);
}
Widget dropdown() {
return GestureDetector(
key: actionKey,
onTap: () {
setState(() {
isPressed = !isPressed;
});
if (isPressed == false) {
overlayEntry = _overlayEntryBuilder();
Overlay.of(context)!.insert(overlayEntry!);
}
},
child: Container(
width: double.infinity,
height: 50.h,
decoration: BoxDecoration(
border: Border.all(color: style.e8e8e8),
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.only(left: 16.w, right: 13.w),
child: Row(
children: [
picked.isEmpty ? pickedEmpty() : pickedNotEmpty(),
const Spacer(),
const Icon(
Icons.arrow_drop_down,
color: style.bdbdbd,
),
],
),
),
);
}
Widget pickedEmpty() {
return Text(
"Možete obeležiti više aktivnosti",
style: TextStyle(
fontSize: 16.sp,
color: style.bdbdbd,
fontWeight: FontWeight.w400,
),
);
}
Widget pickedNotEmpty() {
List<Widget> list = <Widget>[];
for (var i = 0; i < picked.length; i++) {
list.add(
Padding(
padding: EdgeInsets.only(right: 5.w),
child: Text(
picked[i],
style: TextStyle(
fontSize: 16.sp,
color: style.bdbdbd,
fontWeight: FontWeight.w400,
),
),
),
);
}
return Row(children: list);
}
Widget dropdownExpanded() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: style.purpleMain),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
GestureDetector(
onTap: () {
setState(() {
isPressed = !isPressed;
});
overlayEntry?.remove();
},
child: Container(
width: double.infinity,
height: 50.h,
padding: EdgeInsets.only(left: 16.w, right: 13.w),
child: Row(
children: [
picked.isEmpty ? pickedEmpty() : pickedNotEmpty(),
const Spacer(),
const Icon(
Icons.arrow_drop_up,
color: style.bdbdbd,
),
],
),
),
),
const Divider(
height: 0,
thickness: 1,
color: style.e8e8e8,
indent: 17,
endIndent: 17,
),
Column(
children: [
for (int i in ints) buildRows(i),
],
),
],
),
);
}
Here are results
This is what I want to accomplish
So I just want to move down this expanded dropdown and how to update these booles in the overlay if I don't use overlay it's working as it should but I need to open that dropdown on the top of another content. Thanks for the help.
Use smart_select it is fully customizable and you can achieve the design you want easily using this library.
Updated Answer
Regarding the UI, it is like an Expansions Tile Widget in flutter. You can implement that dropdown with expansions tile and pass list of items in children,
for expand, collapse tile after select each item, you can create a global key and control that in UI.
final GlobalKey<AppExpansionTileState> expansionTile = new GlobalKey();
collapse → expansionTile.currentState.collapse();
ExpansionTile(
title: Text(
"Možete obeležiti više aktivnosti",
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w500),
),
children: <Widget>[
// put items here
],
),
smaple :
Widget customDropDown() => Container(
// color: Colors.white,
padding: const EdgeInsets.all(10),
child: ListTileTheme(
dense: true,
child: ExpansionTile(
title: const Text(
"Možete obeležiti više aktivnosti",
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w500),
),
children: <Widget>[
Container(
width: double.infinity,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(bottom: Radius.circular(20))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: const [
ListTile(
leading: Icon(Icons.ac_unit),
title: Text("something"),
),
ListTile(
leading: Icon(Icons.ac_unit),
title: Text("something"),
),
ListTile(
leading: Icon(Icons.ac_unit),
title: Text("something"),
),
ListTile(
leading: Icon(Icons.ac_unit),
title: Text("something"),
)
],
),
),
)
],
),
),
);
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: SafeArea(
child: Stack(
children: [pageDesign(), customDropDown()],
),
),
),
);
}
fakeWidget(color) => Container(
height: 100,
width: double.infinity,
color: color,
child: const Center(
child: Text("widget1"),
),
);
Widget pageDesign() => Column(
children: [
/* you should control this size in diffrent orientation and for big size
device to handle responsive
*/
const SizedBox(
height: 80,
),
fakeWidget(
Colors.green,
),
fakeWidget(
Colors.yellow,
),
fakeWidget(
Colors.orange,
),
fakeWidget(
Colors.blue,
),
],
);
Image looks like this
I have this widget that acts as a tab bar but was not create with a Tab bar. I want that widget to behave like a collapsing toolbar. Can anyone please help me out? This is the code
This is the code for the animated container which I want to behave like a collapsing toolbar...
Here is the code:
class Watching extends StatefulWidget {
final set_state;
const Watching(this.set_state, {Key? key}) : super(key: key);
#override
_WatchState createState() => _WatchState();
}
class _WatchState extends State<Watching> with TickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> animation, animation2;
final double startingHeight = 20.0;
List pages = [];
int index = 0;
GlobalKey _bottomNavigationKey = GlobalKey();
List titles = [feed, trending, explore];
#override
void initState() {
super.initState();
pages = [
MainPage(
set_state: widget.set_state,
),
TrendingPage(set_state: widget.set_state),
ExplorePage()
];
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 5));
animation = Tween<double>(begin: 125, end: 150).animate(_controller);
animation2 = Tween<double>(begin: 125, end: 150).animate(_controller);
_controller.forward(from: 0.0);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize: Size(MediaQuery.of(context).size.width, twoFiftyDp),
child: Container(
color: Colors.teal,
child: Stack(
children: [
AnimatedContainer(
duration: Duration(milliseconds: 400),
width: animation.value,
height: animation.value <= 150 ? 150 : animation.value,
decoration: BoxDecoration(
color: Colors.teal,
),
),
Align(
alignment: Alignment.bottomRight,
child: RotationTransition(
turns: AlwaysStoppedAnimation(0 / 360),
child: ClipPath(
clipper: LeftRoundedClipper(flip: true),
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
width: animation2.value - 10,
height: animation2.value *
MediaQuery.of(context)
.size
.height, //<= 150 ? 200 :animation.value,
decoration: BoxDecoration(
color: Colors.teal,
gradient: LinearGradient(
colors: [
Colors.lightGreen.withGreen(10),
Colors.greenAccent,
Colors.greenAccent
],
)),
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: CurvedNavigationBar(
key: _bottomNavigationKey,
height: 65,
color: Colors.white,
buttonBackgroundColor: Colors.transparent,
backgroundColor: Colors.transparent,
animationCurve: Curves.easeInOut,
animationDuration: Duration(milliseconds: 600),
index: 0,
onTap: (value) {
switch (value) {
case 0:
/* animation = Tween<double>(begin: 125, end: 0)
.animate(_controller);*/
animation2 = Tween<double>(begin: 125, end: 500)
.animate(_controller);
break;
case 1:
animation2 = Tween<double>(begin: 125, end: 700)
.animate(_controller);
/* animation = Tween<double>(begin: -10, end: 50)
.animate(_controller);*/
break;
case 2:
animation = Tween<double>(begin: 125, end: 500)
.animate(_controller);
animation2 = Tween<double>(begin: 150, end: 200)
.animate(_controller);
break;
}
setState(() {
index = value;
});
},
items: [
Text(
Main,
style: index == 0
? TextStyle(
color: Colors.white, fontWeight: FontWeight.bold)
: TextStyle(color: Colors.black54),
),
Text(
trending,
style: index == 1
? TextStyle(
color: Colors.white, fontWeight: FontWeight.bold)
: TextStyle(color: Colors.black54),
),
Text(
explore,
style: index == 2
? TextStyle(
color: Colors.white, fontWeight: FontWeight.bold)
: TextStyle(color: Colors.black54),
),
],
),
),
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding:
const EdgeInsets.only(bottom: sixtyDp, left: tenDp),
child: ListTile(
title: Text(
titles[index],
style: TextStyle(
fontSize: fiftyDp,
fontWeight: FontWeight.bold,
color: Colors.white),
)),
)),
Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.only(top: fiftyDp, left: twentyDp),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
index == 0
? ShowIcon(
iconName: 'assets/icons/User.png',
onIconTap: () {},
)
: SizedBox(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(right: fourteenDp),
child: ShowIcon(
iconName: 'assets/icons/MagnifyingGlass.png',
onIconTap: () {},
),
),
Padding(
padding: const EdgeInsets.only(right: fourteenDp),
child: ShowIcon(
iconName: 'assets/icons/BellRinging.png',
onIconTap: () {},
),
),
ShowIcon(
iconName: 'assets/icons/PaperPlane.png',
onIconTap: () {},
),
],
),
)
// Spacer(),
],
),
),
)
],
),
),
),
// body: pages[index],
body: DefaultTabController(
length: pages.length, child: NestedScrollView(headerSliverBuilder: (context, innerBoxIsScrolled) {
return <Widget>[
SliverList(delegate: SliverChildListDelegate([]))
];
}, body: pages[index]),
),
),
);
}
}
So this is the main code where I fetch the data from json and update my UI.
I have placed" //Area of Interest " comments where the code related to the problem lies.
class MainScreen extends StatefulWidget {
final curLocdata;
MainScreen({this.curLocdata});
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
Weather weather = Weather();
var cityName;
int temp;
int temp_min;
int temp_max;
Icon weatherIcon;
//Area of Interest 1
RotateAnimatedTextKit textSum;//created a widget of RotateAnimatedTextKit library.
String st;
//Area of Interest 2
#override
void initState() {
// TODO: implement initState
super.initState();
updateUI(widget.curLocdata);//calling update function to rebuild my UI state with new data
}
void updateUI(data) {
setState(() {
if (data == null) {
temp = 0;
cityName = 'Error';
weatherIcon = Icon(Icons.error);
return;
}
cityName = data['name'];
temp = data['main']['temp'].toInt();
temp_min = data['main']['temp_min'].toInt();
temp_max = data['main']['temp_max'].toInt();
var condition = data['weather'][0]['id'];
weatherIcon = weather.getIcon(condition);
textSum = weather.getMessage(temp);//Area of Interest 3
st = weather.subtext(condition);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: SafeArea(
child: Column(
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
FlatButton(
onPressed: () async {
updateUI(await Network().getData());
},
child: Icon(
FontAwesomeIcons.locationArrow,
),
),
SizedBox(
width: 180.0,
child: TextLiquidFill(
waveDuration: Duration(seconds: 3),
loadDuration: Duration(seconds: 10),
text: 'OpenWeather',
waveColor: Colors.red,
boxBackgroundColor: Color(0xFF1B1B1D),
textStyle: TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.bold,
fontFamily: 'Source Sans Pro',
),
boxHeight: 50.0,
),
),
FlatButton(
onPressed: () async {
String SName = await Navigator.push(context,
MaterialPageRoute(builder: (context) {
return Search();
}));
if (SName != null) {
updateUI(await Network().getDataName(
SName));
}
},
child: Icon(
Icons.add,
color: Colors.white,
size: 40,
),
),
],
),
),
Padding(
padding: EdgeInsets.fromLTRB(50, 50, 50, 0),
child: Row(
children: <Widget>[
SizedBox(
// margin: EdgeInsets.fromLTRB(0, 50, 260, 0),
child: TypewriterAnimatedTextKit(
totalRepeatCount: 200,
isRepeatingAnimation: true,
speed: Duration(milliseconds: 700),
text: [cityName,],
textAlign: TextAlign.left,
textStyle: TextStyle(
fontSize: 20,
fontFamily: 'Source Sans Pro',
),
),
),
],
)),
Expanded(
flex: 9,
child: Container(
margin: EdgeInsets.fromLTRB(50, 30, 50, 80),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
//mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
flex: 2,
child: Padding(
padding: EdgeInsets.only(left: 20),
child: Text(
'$temp°',
style: TextStyle(
fontSize: 80,
fontWeight: FontWeight.bold,
fontFamily: 'Source Sans Pro',
),
),
),
),
Expanded(
flex: 2,
child: Padding(
padding: EdgeInsets.only(left: 20),
child: Text(
st,
style: TextStyle(
fontSize: 30,
fontFamily: 'Source Sans Pro',
color: Colors.grey[500]),
),
),
),
Padding(
padding: EdgeInsets.fromLTRB(20, 0, 0, 50),
child: Container(
child: textSum,//Used this textSum to show my animated text. problem
Area of Interest 4
),
),
Expanded(
child: SizedBox(
//width: double.infinity,
//height: 100,
child: Divider(
color: Colors.red,
),
),
),
Row(
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.fromLTRB(20, 0, 0, 38),
child: Text(
'$temp_min° - $temp_max°',
style: TextStyle(
fontSize: 20,
color: Colors.grey[500],
fontFamily: 'Source Sans Pro',
),
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.fromLTRB(20, 0, 0, 20),
//padding: const EdgeInsets.all(8.0),
child: AvatarGlow(
endRadius: 30.0, //required
child: Material(
//required
elevation: 0.0,
shape: CircleBorder(),
child: CircleAvatar(
//backgroundColor: Colors.grey[100],
child: weatherIcon
// radius: 40.0,
//shape: BoxShape.circle
),
),
),
),
)
],
)`enter code here`
],
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Color(0xFF0C0C0C),
),
),
),
Now the weather.dart file from where I am returning RotateAnimatedTextKit widget depending upon the condition
class Weather{
//This return RotateAnimatedTextKit which is then held by textSum and is put as a child inside a container in MainScreen
RotateAnimatedTextKit getMessage(int temp) {
if (temp > 25) {
return RotateAnimatedTextKit(
isRepeatingAnimation: true,
totalRepeatCount: 200,
transitionHeight: 40,
text: ['It\'s 🍦','time and','drink plenty','of water'],
textStyle: TextStyle(fontSize: 30.0, fontFamily: "Source Sans Pro", color: Colors.red),
textAlign: TextAlign.start,
alignment: AlignmentDirectional.topStart // or Alignment.topLeft
);
} else if (temp > 20) {
return RotateAnimatedTextKit(
isRepeatingAnimation: true,
totalRepeatCount: 200,
transitionHeight: 50,
text: ['Time for','shorts','👕','but keep','some warm','clothes handy'],
textStyle: TextStyle(fontSize: 30.0, fontFamily: "Source Sans Pro", color: Colors.red),
textAlign: TextAlign.start,
alignment: AlignmentDirectional.bottomStart// or Alignment.topLeft
);
} else if (temp < 10) {
return RotateAnimatedTextKit(
isRepeatingAnimation: true,
totalRepeatCount: 200,
transitionHeight: 50,
text: ['You\'ll need','a 🧣','and','a 🧤','and a hot', 'soup and turkey'],
textStyle: TextStyle(fontSize: 30.0, fontFamily: "Source Sans Pro", color: Colors.red),
textAlign: TextAlign.start,
alignment: AlignmentDirectional.bottomStart // or Alignment.topLeft
);
} else {
return RotateAnimatedTextKit(
isRepeatingAnimation: true,
transitionHeight: 50,
totalRepeatCount: 200,
text: ['Bring a','🧥','just in case','and also avoid', 'cold breeze','and cold drinks'],
textStyle: TextStyle(fontSize: 30.0, fontFamily: "Source Sans Pro", color: Colors.red),
textAlign: TextAlign.start,
alignment: AlignmentDirectional.bottomStart // or Alignment.topLeft
);
}
}
The thing is that the UI doesn't get updated even when the conditions are different. So any Solutions to why the widget tree is not updating? But it runs only the default text. Also, the cityName which is under the TextLiquidFill doesn't get updated.
Short answer:
Use Keys
Example:
import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:flutter/material.dart';
class MyAnimatedText extends StatefulWidget {
const MyAnimatedText({Key? key}) : super(key: key);
#override
State<MyAnimatedText> createState() => _MyAnimatedTextState();
}
class _MyAnimatedTextState extends State<MyAnimatedText> {
bool isDarkMode = true;
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: isDarkMode ? Colors.grey[850] : Colors.amber[300]),
child: Column(
children: [
Container(
alignment: Alignment.topRight,
child: IconButton(
onPressed: () {
setState(() {
isDarkMode = !isDarkMode;
});
},
icon: Icon(isDarkMode ? Icons.light_mode : Icons.dark_mode))),
Padding(
padding: const EdgeInsets.all(15.0),
child: AnimatedTextKit(
key: ValueKey<bool>(isDarkMode),
animatedTexts: [
TypewriterAnimatedText(
isDarkMode ? 'Have a nice evening ;)' : 'Have a nice day :)',
cursor: isDarkMode ?'>':'<',
textStyle: TextStyle(
fontSize: 38,
color: isDarkMode ? Colors.amber[300] : Colors.grey[850]),
speed: const Duration(milliseconds: 100),
),
],
),
),
],
),
);
}
}
Result:
Background Information
I faced the same problem, when I tried to implement a dark / light change. The background color was defined in an other widget and changed, the font color was defined in the TypewriterAnimatedText Widget and only changed in the second loop. The color was not changing in the runnig animation.
Solution: use Keys
The Animation does not change beacause Flutter tries to keep the state of an StatefulWidget and the AnimatedTextKit is a Stateful Widget.
To force a rebuild you can use a Key.
a nice article can be found here: How to force a Widget to redraw in Flutter?
You can use WidgetsBinding.instance.addPostFrameCallback
For detail, you can reference https://www.didierboelens.com/faq/week2/
code snippet
#override
void initState(){
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
updateUI(widget.curLocdata);
});
}