[I have a list of location documents in Firebase and Algolia that are displayed on my Flutter app's google map page as markers. There are over 50 location documents currently in my Firebase, but at a time only 20 markers are displayed on the map no matter what. There are no limitations set anywhere, but there is a maximum of 20 markers. In order for there to be more than 20 markers, I need to implement clusters in my map page but I am not able to. With this code being the code for my map page, how would I implement clusters?
import '../auth/auth_util.dart';
import '../backend/backend.dart';
import '../flutter_flow/flutter_flow_ad_banner.dart';
import '../flutter_flow/flutter_flow_google_map.dart';
import '../flutter_flow/flutter_flow_theme.dart';
import '../flutter_flow/flutter_flow_util.dart';
import '../flutter_flow/flutter_flow_widgets.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:google_fonts/google_fonts.dart';
class FindCourtWidget extends StatefulWidget {
const FindCourtWidget({
Key? key,
this.searchTerm,
}) : super(key: key);
final String? searchTerm;
#override
_FindCourtWidgetState createState() => _FindCourtWidgetState();
}
class _FindCourtWidgetState extends State<FindCourtWidget> {
LatLng? currentUserLocationValue;
final scaffoldKey = GlobalKey<ScaffoldState>();
LatLng? googleMapsCenter;
final googleMapsController = Completer<GoogleMapController>();
#override
void initState() {
super.initState();
// On page load action.
SchedulerBinding.instance.addPostFrameCallback((_) async {
currentUserLocationValue =
await getCurrentUserLocation(defaultLocation: LatLng(0.0, 0.0));
final usersUpdateData = createUsersRecordData(
latLngLocation: currentUserLocationValue,
);
await currentUserReference!.update(usersUpdateData);
});
getCurrentUserLocation(defaultLocation: LatLng(0.0, 0.0), cached: true)
.then((loc) => setState(() => currentUserLocationValue = loc));
WidgetsBinding.instance.addPostFrameCallback((_) => setState(() {}));
}
#override
Widget build(BuildContext context) {
if (currentUserLocationValue == null) {
return Container(
color: FlutterFlowTheme.of(context).primaryBackground,
child: Center(
child: SizedBox(
width: 50,
height: 50,
child: SpinKitRotatingCircle(
color: Color(0xFFF25454),
size: 50,
),
),
),
);
}
return FutureBuilder<List<CourtsRecord>>(
future: CourtsRecord.search(
term: '*',
location: getCurrentUserLocation(
defaultLocation: LatLng(37.4298229, -122.1735655)),
),
builder: (context, snapshot) {
// Customize what your widget looks like when it's loading.
if (!snapshot.hasData) {
return Center(
child: SizedBox(
width: 50,
height: 50,
child: SpinKitRotatingCircle(
color: Color(0xFFF25454),
size: 50,
),
),
);
}
List<CourtsRecord> findCourtCourtsRecordList = snapshot.data!;
return Title(
title: 'findCourt',
color: FlutterFlowTheme.of(context).primaryColor,
child: Scaffold(
key: scaffoldKey,
resizeToAvoidBottomInset: false,
body: Stack(
children: [
Stack(
children: [
Stack(
alignment: AlignmentDirectional(0, 0),
children: [
Align(
alignment: AlignmentDirectional(0, 0),
child: FlutterFlowGoogleMap(
controller: googleMapsController,
onCameraIdle: (latLng) =>
setState(() => googleMapsCenter = latLng),
initialLocation: googleMapsCenter ??=
currentUserLocationValue!,
markers: findCourtCourtsRecordList
.map(
(findCourtCourtsRecord) =>
FlutterFlowMarker(
findCourtCourtsRecord.reference.path,
findCourtCourtsRecord.location!,
() async {
context.pushNamed(
'courtDetailsCopy',
params: {
'court2': serializeParam(
findCourtCourtsRecord,
ParamType.Document,
),
}.withoutNulls,
extra: <String, dynamic>{
'court2': findCourtCourtsRecord,
},
);
},
),
)
.toList(),
markerColor: GoogleMarkerColor.red,
mapType: MapType.normal,
style: GoogleMapStyle.standard,
initialZoom: 14,
allowInteraction: true,
allowZoom: true,
showZoomControls: false,
showLocation: true,
showCompass: false,
showMapToolbar: false,
showTraffic: false,
centerMapOnMarkerTap: true,
),
),
],
),
Align(
alignment: AlignmentDirectional(0, 0.8),
child: FFButtonWidget(
onPressed: () async {
context.pushNamed(
'addCourt',
extra: <String, dynamic>{
kTransitionInfoKey: TransitionInfo(
hasTransition: true,
transitionType:
PageTransitionType.bottomToTop,
),
},
);
},
text: 'Create Location',
icon: Icon(
Icons.add_circle,
color: FlutterFlowTheme.of(context).white,
size: 30,
),
options: FFButtonOptions(
width: 200,
height: 60,
color: FlutterFlowTheme.of(context).primaryColor,
textStyle:
FlutterFlowTheme.of(context).subtitle2.override(
fontFamily: 'Overpass',
color: FlutterFlowTheme.of(context).white,
fontSize: 18,
),
borderSide: BorderSide(
color: Colors.transparent,
width: 1,
),
borderRadius: BorderRadius.circular(50),
),
),
),
Align(
alignment: AlignmentDirectional(-1, -1),
child: Container(
width: MediaQuery.of(context).size.width,
height: 262,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
FlutterFlowTheme.of(context).white,
Color(0x00FFFFFF)
],
stops: [0, 1],
begin: AlignmentDirectional(0, -1),
end: AlignmentDirectional(0, 1),
),
),
alignment: AlignmentDirectional(0, 0),
child: Padding(
padding:
EdgeInsetsDirectional.fromSTEB(0, 20, 0, 0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Align(
alignment: AlignmentDirectional(0, 0),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30, 30, 30, 0),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Align(
alignment: AlignmentDirectional(
-0.68, -0.75),
child: Text(
'Hello ',
textAlign: TextAlign.start,
style: FlutterFlowTheme.of(context)
.title2
.override(
fontFamily: 'Overpass',
fontSize: 30,
),
),
),
Align(
alignment:
AlignmentDirectional(0.63, -0.75),
child: AuthUserStreamWidget(
builder: (context) => Text(
currentUserDisplayName
.maybeHandleOverflow(
maxChars: 13,
replacement: '…',
),
textAlign: TextAlign.start,
style:
FlutterFlowTheme.of(context)
.title2
.override(
fontFamily: 'Overpass',
fontSize: 30,
),
),
),
),
],
),
),
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Align(
alignment:
AlignmentDirectional(-0.05, -0.85),
child: Text(
'Find a great sport near you in 2 minutes',
style: FlutterFlowTheme.of(context)
.bodyText1
.override(
fontFamily: 'Overpass',
color: Color(0xFF504848),
fontSize: 16,
fontWeight: FontWeight.w300,
),
),
),
],
),
],
),
),
),
),
Align(
alignment: AlignmentDirectional(0, 1),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Align(
alignment: AlignmentDirectional(0, 1),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Align(
alignment: AlignmentDirectional(0, 1),
child: FlutterFlowAdBanner(
width:
MediaQuery.of(context).size.width,
height: 50,
showsTestAd: true,
iOSAdUnitID:
'ca-app-pub-4806655917667448/9288549735',
androidAdUnitID:
'ca-app-pub-4806655917667448/4144034269',
),
),
),
],
),
),
],
),
),
],
),
],
),
));
},
);
}
}
I have tried using the Fluster package to implement clusters but my current knowledge in flutter/dart is not allowing me to go further in my implementation.
Related
I'm a beginner at app dev and I'm trying out flutter. I'm currently having a problem with positioning my background on the project that I am currently testing out.I'm following a UI kit that I am trying to copy for the purpose of practicing, but I am having problem with the UI.
I tried using stack but the whole screen is wrapped with its children and not taking up space. it looks like this:
and this is what I wanted to do:
This is the background that I wanted to put in my app, it is not literally a background or wallpaper because of its size. I just needed this to be placed at the bottom of the screen or background:
this is the code that I currently have:
import 'package:audit_finance_app/constant/theme.dart';
import 'package:audit_finance_app/widgets/widgets.dart';
import 'package:audit_finance_app/screens/homescreen.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;
class SignInPage extends StatefulWidget {
const SignInPage({super.key});
#override
State<SignInPage> createState() => _SignInPageState();
}
class _SignInPageState extends State<SignInPage> {
late List<String> inputPass;
String defaultPass = '1234';
#override
void initState() {
inputPass = [];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: <Widget>[
const SizedBox(
width: double.maxFinite,
height: double.maxFinite,
child: Image(
image: AssetImage('assets/background.png'),
),
),
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
AuditTheme.primaryColor,
AuditTheme.secondaryColor,
],
),
),
),
leadingWidth: 100,
leading: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
child: Row(
children: const [
Expanded(
child: ImageIcon(
AssetImage('assets/logo/white_logo.png'),
),
),
Text(
'Audit',
style: TextStyle(fontSize: 20),
),
],
),
),
title: const Text('Sign In'),
centerTitle: true,
actions: [
Transform(
alignment: Alignment.center,
transform: Matrix4.rotationY(math.pi),
child: IconButton(
onPressed: () {},
icon: const Icon(Icons.sort),
),
),
],
),
SliverList(
delegate: SliverChildListDelegate(
[
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Widgets().sixedBoxHeight(50),
Column(
children: [
const CircleAvatar(
radius: 35,
backgroundImage:
AssetImage('assets/logo/audit_logo.png'),
),
Widgets().sixedBoxHeight(10),
const Text(
'Ledjoric Vermont',
style: TextStyle(fontSize: 20),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
pinIconTest(inputPass.isNotEmpty
? Colors.black
: Colors.grey),
pinIconTest(inputPass.length >= 2
? Colors.black
: Colors.grey),
pinIconTest(inputPass.length >= 3
? Colors.black
: Colors.grey),
pinIconTest(inputPass.length == 4
? Colors.black
: Colors.grey),
],
),
Card(
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
numPad(const Text('1'), () => inputPin('1')),
numPad(const Text('2'), () => inputPin('2')),
numPad(const Text('3'), () => inputPin('3')),
],
),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
numPad(const Text('4'), () => inputPin('4')),
numPad(const Text('5'), () => inputPin('5')),
numPad(const Text('6'), () => inputPin('6')),
],
),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
numPad(const Text('7'), () => inputPin('7')),
numPad(const Text('8'), () => inputPin('8')),
numPad(const Text('9'), () => inputPin('9')),
],
),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(
width: 100,
height: 100,
),
numPad(const Text('0'), () => inputPin('0')),
numPad(
const Icon(Icons.backspace_sharp),
() => deletePin(),
),
],
),
],
),
),
],
),
],
),
),
],
),
],
),
);
}
Widget pinIconTest(Color color) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(
Icons.circle,
size: 35,
color: color,
),
);
}
Widget numPad(Widget widget, void Function() function) {
return SizedBox(
width: 100,
height: 100,
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.grey,
textStyle: const TextStyle(
fontSize: 30,
),
),
onPressed: function,
child: widget,
),
);
}
void inputPin(String value) {
setState(() {
inputPass.length != 4 ? inputPass.add(value) : null;
inputPass.length == 4 ? checkPass() : null;
});
print(inputPass);
}
void checkPass() {
var stringList = inputPass.join('');
if (stringList == defaultPass) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const HomeScreen(),
),
);
}
print(stringList);
}
void deletePin() {
setState(() {
inputPass.isNotEmpty ? inputPass.removeLast() : null;
});
print(inputPass);
}
}
I was missing the fact you want to place at the bottom that background, however to achieve that you can do it as the code below shows:
class SignInPage extends StatefulWidget {
const SignInPage({super.key});
#override
State<SignInPage> createState() => _SignInPageState();
}
class _SignInPageState extends State<SignInPage> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: <Widget>[
const Align(
alignment: Alignment.bottomCenter,
child: Image(
image: AssetImage('assets/background.png'),
),
),
//Other child here
],
),
);
}
}
And this is the result:
You can use the example below for the status bar. I don't know about the real problem.
You can try using this way for gradient color
SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
statusBarBrightness: Brightness.dark,
statusBarGradient: LinearGradient(
colors: [Colors.red, Colors.blue],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
);
You need to wrap your image in a Positioned Widget.
Positioned(bottom: 0.0,
child: const SizedBox(
width: double.maxFinite,
height: double.maxFinite,
child: Image(
image: AssetImage('assets/background.png'),
),
),
Or you can use the alignment property of the Stack to stick everything onto the bottom. I'm not sure this is exactly what you want though.
body: Stack(
alignment: Alignment.bottomCenter,
fit: StackFit.expand,
children: <Widget>[
const SizedBox(
width: double.maxFinite,
height: double.maxFinite,
child: Image(
image: AssetImage('assets/background.png'),
),
),
I want to design this layout:
I am using a expandable listview inside a column. I was add ListView.builder inside Expanded but still problem continue.
What is the wrong?
This is my code:
Parent page:
class LearningCoursePage extends StatefulWidget {
String courseId;
LearningCoursePage({Key? key, required this.courseId}) : super(key: key);
#override
State<LearningCoursePage> createState() => _LearningCoursePageState();
}
class _LearningCoursePageState extends State<LearningCoursePage> {
CourseModel? courseModel;
Future<void> _loadResource() async {
courseModel = await Get.find<CourseController>().getCourseById(widget.courseId);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _loadResource(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: AppColors.yellowColor,
title: courseModel != null ? Text(courseModel!.name.toString()) : Text("Loading..."),
leading: IconButton(
icon: Icon(Icons.close),
onPressed: () {},
),
),
body: GetBuilder<AuthController>(builder: (authController) {
return authController.userLoggedIn() ? Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Dimensions.radius20),
boxShadow: [
BoxShadow(
color: Color(0xFFe8e8e8),
blurRadius: 5.0,
offset: Offset(0, 5)
),
BoxShadow(
color: Colors.white,
offset: Offset(-5, 0)
),
BoxShadow(
color: Colors.white,
offset: Offset(5, 0)
),
]
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 40,),
BigText(text: "Vui lòng đăng nhập để xác minh tài khoản"),
SizedBox(height: 20,),
SizedBox(height: 20,),
GestureDetector(
onTap: (){
Get.toNamed(RouteHelper.getSignInPage());
},
child: Container(
width: Dimensions.screenWidth/2,
height: Dimensions.screenHeight/13,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Dimensions.radius15),
color: AppColors.yellowColor
),
child: Center(
child: BigText(
text: "ĐĂNG NHẬP",
size: Dimensions.font20+Dimensions.font20/2,
color: Colors.white,
),
),
),
),
SizedBox(height: 40,),
],
),
),
) :
SingleChildScrollView(
child: Container(
child: Column(
children: [
Container(
height: 400,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage("assets/image/test.jpg")
)
),
),
Container(
width: double.infinity,
color: AppColors.mainBlackColor,
child: Container(
margin: EdgeInsets.only(left: Dimensions.width20, right: Dimensions.width20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 30,),
BigText(text: (courseModel != null ? courseModel!.name.toString().toUpperCase() : "Loading..."), size: 20, color: AppColors.whiteColor,),
SizedBox(height: 30,),
BigText(text: "Bạn hoàn thành 3 trong 59 bài giảng", size: 16, color: AppColors.whiteColor,),
SizedBox(height: 30,),
LinearPercentIndicator(
animation: true,
animationDuration: 1000,
lineHeight: 40.0,
backgroundColor: Colors.white,
percent: 0.2,
padding: EdgeInsets.only(right: 0),
center: Text("20.0%"),
trailing: Container(
color: AppColors.yellowColor,
padding: EdgeInsets.only(left: 10, right: 10),
alignment: Alignment.center,
height: 40,
child: SmallText(text: "hoàn thành", size: 16, color: AppColors.whiteColor,),
),
linearStrokeCap: LinearStrokeCap.butt,
progressColor: AppColors.yellowColor,
),
SizedBox(height: 30,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SelectButtonWidget(text: 'Vào học ngay', isSelect: true, width: Dimensions.screenWidth/2.5,),
SelectButtonWidget(text: 'Hướng dẫn học', width: Dimensions.screenWidth/2.5,),
],
),
SizedBox(height: 30,),
SelectButtonWidget(text: 'Xem mẫu chứng chỉ hoàn thành',),
SizedBox(height: 60,),
],
),
)
),
Container(
width: double.infinity,
color: AppColors.greyColor,
child: Container(
margin: EdgeInsets.only(left: Dimensions.width20, right: Dimensions.width20),
child: Column(
children: [
SizedBox(height: 30,),
LearningCourseBody(courseModel: courseModel,)
],
),
),
)
],
),
),
);
}),
);
},
);
}
}
The body page (cluster layout with gray background color):
class LearningCourseBody extends StatefulWidget {
CourseModel? courseModel;
LearningCourseBody({Key? key, this.courseModel}) : super(key: key);
#override
State<LearningCourseBody> createState() => _LearningCourseBodyState();
}
class _LearningCourseBodyState extends State<LearningCourseBody> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SelectButtonWidget(text: 'Tổng quan', isSelect: true, width: Dimensions.screenWidth/3.5,),
SelectButtonWidget(text: 'Bài học', textColor: AppColors.mainBlackColor, width: Dimensions.screenWidth/3.5,),
SelectButtonWidget(text: 'Tài liệu', textColor: AppColors.mainBlackColor, width: Dimensions.screenWidth/3.5,),
],
),
SizedBox(height: 30,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 5,),
SelectButtonWidget(text: 'Hỏi & đáp', textColor: AppColors.mainBlackColor, width: Dimensions.screenWidth/3.5,),
SizedBox(width: 5,),
SelectButtonWidget(text: 'Ghi chép', textColor: AppColors.mainBlackColor, width: Dimensions.screenWidth/3.5,),
SizedBox(width: 5,),
],
),
SizedBox(height: 60,),
// OverviewCourse(courseModel: widget.courseModel, rating: 5,)
(widget.courseModel != null && widget.courseModel!.particalsCourse != null) ? Expanded(child: ExpandableListViewWidget(courseModel: widget.courseModel!,)) : Text('Loading....'),
],
);
}
}
And this is the expandable lisview:
class ExpandableListViewWidget extends StatefulWidget {
CourseModel courseModel;
ExpandableListViewWidget({Key? key, required this.courseModel}) : super(key: key);
#override
State<ExpandableListViewWidget> createState() => _ExpandableListViewWidgetState();
}
class _ExpandableListViewWidgetState extends State<ExpandableListViewWidget> {
List<Widget> _buildExpandList(int index) {
List<Widget> res = [];
if (widget.courseModel.particalsCourse![index].lessons == null) return res;
for (var sub in widget.courseModel.particalsCourse![index].lessons!) {
var s = ListTile(
title: Text(sub.title!),
leading: Icon(Icons.play_circle),
trailing: sub.isTrial! ? SmallText(text: 'học thử', color: AppColors.yellowColor,) : Text(sub.timeLearning!.toString()),
);
res.add(s);
}
return res;
}
#override
Widget build(BuildContext context) {
List<bool> tileExpanded = List<bool>.generate(widget.courseModel.particalsCourse!.length, (index) => false);
return ListView.builder(
itemCount: widget.courseModel.particalsCourse!.length,
itemBuilder: (context, index) {
return Container(
child: ExpansionTile(
title: Text(widget.courseModel.particalsCourse![index].title!),
controlAffinity: ListTileControlAffinity.leading,
leading: Icon(
tileExpanded[index] ? Icons.arrow_drop_up : Icons.arrow_drop_down
),
children: _buildExpandList(index),
onExpansionChanged: (bool expanded) {
setState(() => tileExpanded[index] = expanded);
},
),
);
}
);
}
}
Hope your help? Thanks!!!
Finally, I was resolve this problem. This is my step:
Step 1: Column use mainAxisSize: MainAxisSize.min
Step 2: Change Expanded to Flexible
Step 3: Adding this two lines in ExpandableListViewWidget
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
...
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 am trying to show some data from the database and my app must contain UI like this.
But I am encountering this kind of problem.
Problems:
The text is overflowing and not wrapping (I tried to use Flexible and Expanded but it produces more exceptions, mostly of non-zero flex and so on)
The list needs fixed height and width, whereas I need height to match_parent. double.infinity don't work as well.
Here is my code:
class CategoryDetailPage extends StatefulWidget {
final Category category;
CategoryDetailPage({Key key, this.category}) : super(key: key);
#override
_CategoryDetailPageState createState() => _CategoryDetailPageState();
}
class _CategoryDetailPageState extends State<CategoryDetailPage> {
DatabaseProvider databaseProvider = DatabaseProvider.instance;
List<Phrase> phrases;
final List<Color> _itemColors = [
Color(0xff16a085),
Color(0xff2980b9),
Color(0xff8e44ad),
Color(0xff2c3e50),
Color(0xffd35400),
Color(0xffbdc3c7),
Color(0xff27ae60),
Color(0xfff39c12),
Color(0xff7f8c8d),
Color(0xffc0392b),
];
int _colorCounter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Row(
children: [
Container(
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image(
image: AssetImage("assets/images/categories/${widget.category.image}"),
width: 32,
),
),
),
Padding(
padding: const EdgeInsets.only(left: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Say ${widget.category.name}",
style: TextStyle(fontSize: 24, fontFamily: "Pacifico"),
),
Text(
"\"${widget.category.quote}\" --${widget.category.quoteAuthor} aaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.grey,
fontStyle: FontStyle.italic
),
),
],
),
),
],
),
Row(
children: <Widget>[
RotatedBox(
quarterTurns: -1,
child: Column(
children: <Widget>[
Text(
"Informal",
style: TextStyle(
fontSize: 32,
color: Colors.grey.withOpacity(0.5),
fontFamily: "AbrilFatFace"),
),
],
),
),
Container(
height: 300,
width: 300,
child: FutureBuilder(
future: databaseProvider
.getPhrasesByCategoryId(widget.category.id),
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, i) {
return _buildPhraseItem(snapshot.data[i]);
})
: Center(
child: CircularProgressIndicator(),
);
},
),
),
],
),
],
),
),
),
);
}
Widget _buildPhraseItem(Phrase phrase) {
Random random = Random();
int colorIndex = random.nextInt(_itemColors.length - 1);
Color currentColor = _itemColors[colorIndex];
if (_colorCounter >= 10) _colorCounter = 0;
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PhraseDetail(
phraseToShow: phrase.phrase,
color: currentColor,
)));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 80,
decoration: BoxDecoration(
color: currentColor,
borderRadius: BorderRadius.all(Radius.circular(4)),
boxShadow: [
BoxShadow(
blurRadius: 8,
color: Colors.grey.withOpacity(0.5),
offset: Offset(0, 3))
]),
child: Center(
child: Text(
phrase.phrase,
style: TextStyle(color: Colors.white),
)),
),
),
);
}
}
wrap the second child(Padding) of the first Row with Flexible
wrap the second child(Container) of the second Row with Flexible and remove width: 300 from the container parameters.
import 'dart:math';
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,
),
home: CategoryDetailPage(),
);
}
}
Random random = Random();
class CategoryDetailPage extends StatefulWidget {
CategoryDetailPage({
Key key,
}) : super(key: key);
#override
_CategoryDetailPageState createState() => _CategoryDetailPageState();
}
class _CategoryDetailPageState extends State<CategoryDetailPage> {
final List<Color> _itemColors = [
Color(0xff16a085),
Color(0xff2980b9),
Color(0xff8e44ad),
Color(0xff2c3e50),
Color(0xffd35400),
Color(0xffbdc3c7),
Color(0xff27ae60),
Color(0xfff39c12),
Color(0xff7f8c8d),
Color(0xffc0392b),
];
int _colorCounter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image(
image: NetworkImage(
'https://source.unsplash.com/random',
),
width: 32,
),
),
),
Flexible(
child: Padding(
padding: const EdgeInsets.only(left: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'say congratulations',
style:
TextStyle(fontSize: 24, fontFamily: "Pacifico"),
),
Text(
"At every party there are two kinds of people – those who want to go home and those who don’t. The trouble is, they are usually married to each other. - Ann Landers",
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.grey,
fontStyle: FontStyle.italic),
),
],
),
),
),
],
),
Row(
children: <Widget>[
RotatedBox(
quarterTurns: -1,
child: Column(
children: <Widget>[
Text(
"Informal",
style: TextStyle(
fontSize: 32,
color: Colors.grey.withOpacity(0.5),
fontFamily: "AbrilFatFace"),
),
],
),
),
Flexible(
child: Container(
height: 300,
child: ListView(children: [
_buildPhraseItem(),
_buildPhraseItem(),
_buildPhraseItem(),
_buildPhraseItem(),
]),
),
),
],
),
],
),
),
),
);
}
Widget _buildPhraseItem() {
var colorIndex = random.nextInt(_itemColors.length - 1);
var currentColor = _itemColors[colorIndex];
if (_colorCounter >= 10) _colorCounter = 0;
return InkWell(
onTap: () {
print('Navigator.push');
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 80,
decoration: BoxDecoration(
color: currentColor,
borderRadius: BorderRadius.all(Radius.circular(4)),
boxShadow: [
BoxShadow(
blurRadius: 8,
color: Colors.grey.withOpacity(0.5),
offset: Offset(0, 3))
]),
child: Center(
child: Text(
'phrase.phrase',
style: TextStyle(color: Colors.white),
)),
),
),
);
}
}
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();
},
),
),
),
],
),
),
),
),
),
);
}