I'm pretty new both to Flutter/Dart and Stack Overflow. I started with some trip ETA code and modified it to be an initiative turn tracker for a fantasy RPG tabletop game.
My intent is to keep the End Encounter button on the screen at all times, and have the timeline content scroll in a window above it.
I added a SingleChildScrollView into _TurnSectionState at line 178, but as soon as I add a Container into it I get the overflow. I tried for about 3 hours before asking for help here.
final data = _data(1);
return Scaffold(
appBar: TitleAppBar(widget._title),
body: Center(
child: SingleChildScrollView(
child: SizedBox(
width: 380.0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.all(20.0),
child: _MissionName(
timeSliceInfo: data,
),
),
const Divider(height: 1.0),
_TurnSection(processes: data.deliveryProcesses),
const Divider(height: 1.0),
const Padding(
padding: EdgeInsets.all(20.0),
child: _OnTimeBar(),
),
],
),
),
),
),
);
}
}
// Mission name
class _MissionName extends StatelessWidget {
const _MissionName({
Key? key,
required this.timeSliceInfo,
}) : super(key: key);
final _TimeSliceInfo timeSliceInfo;
#override
Widget build(BuildContext context) {
return Row(
children: [
Text(
missions[currentMission].getTitle(),
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
],
);
}
}
// Lists items in each turn
class _InnerTimeline extends StatefulWidget {
const _InnerTimeline({
required this.messages,
});
final List<ActivationLineItem> messages;
#override
State<_InnerTimeline> createState() => _InnerTimelinePageState();
}
class _InnerTimelinePageState extends State<_InnerTimeline> {
#override
initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
bool isEdgeIndex(int index) {
return index == 0 || index == widget.messages.length + 1;
}
// Interior connector lines
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: FixedTimeline.tileBuilder(
theme: TimelineTheme.of(context).copyWith(
nodePosition: 0,
connectorTheme: TimelineTheme.of(context).connectorTheme.copyWith(
thickness: 1.0,
),
indicatorTheme: TimelineTheme.of(context).indicatorTheme.copyWith(
size: 10.0,
position: 0.5,
),
),
builder: TimelineTileBuilder(
indicatorBuilder: (_, index) =>
!isEdgeIndex(index) ? Indicator.outlined(borderWidth: 1.0) : null,
startConnectorBuilder: (_, index) => Connector.solidLine(),
endConnectorBuilder: (_, index) => Connector.solidLine(),
contentsBuilder: (_, index) {
if (isEdgeIndex(index)) {
return null;
}
// Line item (init value + icon + text)
return Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Row(children: [
Text(widget.messages[index - 1].tick.toString()),
const SizedBox(width: 6),
Image(
image: AssetImage(widget.messages[index - 1].thumbnail),
width: 20,
height: 20),
const SizedBox(width: 6),
Expanded(
child: Text(widget.messages[index - 1].message,
overflow: TextOverflow.ellipsis, maxLines: 1))
]));
},
itemExtentBuilder: (_, index) => isEdgeIndex(index) ? 10.0 : 30.0,
nodeItemOverlapBuilder: (_, index) =>
isEdgeIndex(index) ? true : null,
itemCount: widget.messages.length + 2,
),
),
);
}
}
// Outer timeline (List of turns)
class _TurnSection extends StatefulWidget {
const _TurnSection({Key? key, required processes})
: _processes = processes,
super(key: key);
final List<TurnContents> _processes;
#override
_TurnSectionState createState() => _TurnSectionState();
}
class _TurnSectionState extends State<_TurnSection> {
bool isExpanded = false;
#override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: const TextStyle(
color: Color(0xff9b9b9b), // Nobel Gray
fontSize: 12.5,
),
child: SingleChildScrollView(
child: SizedBox(
height: 480,
child: Column(
children: [
FixedTimeline.tileBuilder(
theme: TimelineThemeData(
nodePosition: 0,
color: const Color(0xff989898), // Spanish Gray
indicatorTheme: const IndicatorThemeData(
position: 0,
size: 20.0, // Outer timeline circle size
),
connectorTheme: const ConnectorThemeData(
thickness: 2.5, // Outer timeline line thickness
),
),
builder: TimelineTileBuilder.connected(
connectionDirection: ConnectionDirection.before,
itemCount: widget._processes.length,
contentsBuilder: (_, index) {
if (widget._processes[index].isCompleted) return null;
return GestureDetector(
onTap: () {
int turnNum = widget._processes[index].getTurnNum();
if (turnNum == currentTurn) {
// Ask if ready for next turn
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Next Turn?'),
content: Text('Ready to start turn ?' +
(currentTurn + 1).toString()),
actions: <Widget>[
TextButton(
onPressed: () =>
Navigator.pop(context, 'Yes'),
child: const Text('Yes'),
),
TextButton(
onPressed: () => Navigator.pop(context, 'No'),
child: const Text('No'),
),
],
),
).then((value) {
if (value == 'Yes') {
currentTurn++; // Move to the next turn
setState(() {}); // Draw the screen
}
});
}
},
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget._processes[index]
.getName(), // Turn name font size
style:
DefaultTextStyle.of(context).style.copyWith(
fontSize: 18.0,
),
),
_InnerTimeline(
messages:
widget._processes[index].getMessages()),
],
),
),
);
},
indicatorBuilder: (_, index) {
if (index <= currentTurn) {
return const DotIndicator(
color: Color(0xff66c97f), // Emerald Green dot
child: Icon(
Icons.check,
color: Colors.white,
size: 12.0,
),
);
} else {
return const OutlinedDotIndicator(
borderWidth: 2.5,
);
}
},
connectorBuilder: (_, index, ___) => SolidLineConnector(
color: index <= currentTurn
? const Color(0xff66c97f) // Emerald Green connector
: null, // Green
),
),
),
],
),
),
),
);
}
}
class _OnTimeBar extends StatelessWidget {
const _OnTimeBar({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
children: [
MaterialButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('On Time!'),
),
);
},
elevation: 0,
shape: const StadiumBorder(),
color: const Color(0xff66c97f),
textColor: Colors.white,
child: const Text('End Encounter'),
),
const Spacer(),
const SizedBox(width: 12.0),
],
);
}
}
/*
List<TurnContents> buildTimeline() {
List<TurnContents> turns;
return turns;
}
*/
_TimeSliceInfo _data(int id) => _TimeSliceInfo(
id: id,
date: DateTime.now(),
deliveryProcesses: ImpulseBuilder().buildTimeline(),
);
class _TimeSliceInfo {
const _TimeSliceInfo({
required this.id,
required this.date,
required this.deliveryProcesses,
});
final int id;
final DateTime date;
final List<TurnContents> deliveryProcesses;```
}
[1]: https://i.stack.imgur.com/mGd5X.png
use Expanded and in SingleChildScrollView add physics:ScrollPhysics()
Expanded(
child: Container(
child: SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: []
)
)
)
);
Related
can someone help me figure out what i'm doing wrong? I have a category screen and I have a list-view of all my parent categories on the left and my main categories filling the remaining space as a drop-down widget to display the sub-categories but the expansion tile is not opening. Below is the code of my category screen
`
import 'package:buyfast/Widget/category/main_category_widget.dart';
import 'package:buyfast/models/category_model.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_iconly/flutter_iconly.dart';
class CategoryScreen extends StatefulWidget {
const CategoryScreen({Key? key}) : super(key: key);
#override
State<CategoryScreen> createState() => _CategoryScreenState();
}
class _CategoryScreenState extends State<CategoryScreen> {
String _title = 'Categories';
String? selectedCategory;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(
selectedCategory==null ? _title : selectedCategory!,
style: const TextStyle(color: Colors.black,fontSize: 16),),
elevation: 0,
backgroundColor: Colors.white,
iconTheme: const IconThemeData(
color: Colors.black54
),
actions: [
IconButton(
onPressed: (){},
icon: const Icon(IconlyLight.search),
),
IconButton(
onPressed: (){},
icon: const Icon(IconlyLight.buy),
),
IconButton(
onPressed: (){},
icon: const Icon(Icons.more_vert),
),
],
),
body: Row(
children: [
Container(
width: 80,
color: Colors.grey.shade300,
child: FirestoreListView<Category>(
query: categoryCollection,
itemBuilder: (context, snapshot) {
Category category = snapshot.data();
return InkWell(
onTap: (){
setState(() {
_title= category.catName!;
selectedCategory = category.catName;
});
},
child: Container(
height: 70,
color: selectedCategory == category.catName ? Colors.white : Colors.grey.shade300,
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 30,
child: CachedNetworkImage(
imageUrl: category.image!,
color: selectedCategory == category.catName ? Theme.of(context).primaryColor:Colors.grey.shade700,
),
),
Text(
category.catName!,
style: TextStyle(
fontSize: 10,
color: selectedCategory == category.catName ? Theme.of(context).primaryColor:Colors.grey.shade700,
),
textAlign: TextAlign.center,
),
],
),
),
),
),
);
},
),
),
MainCategoryWidget(
selectedCat: selectedCategory,
)
],
),
);
}
}
Now my category model to retrieve the categories from Firebase
import 'package:buyfast/firebase_service.dart';
class Category {
Category({this.catName, this.image});
Category.fromJson(Map<String, Object?> json)
: this(
catName: json['catName']! as String,
image: json['image']! as String,
);
final String? catName;
final String? image;
Map<String, Object?> toJson() {
return {
'catName': catName,
'image': image,
};
}
}
FirebaseService _service = FirebaseService();
final categoryCollection = _service.categories.where('active',isEqualTo: true).withConverter<Category>(
fromFirestore: (snapshot, _) => Category.fromJson(snapshot.data()!),
toFirestore: (category, _) => category.toJson(),
);
My category widget
import 'package:buyfast/models/category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_iconly/flutter_iconly.dart';
class CategoryWidget extends StatefulWidget {
const CategoryWidget({Key? key}) : super(key: key);
#override
State<CategoryWidget> createState() => _CategoryWidgetState();
}
class _CategoryWidgetState extends State<CategoryWidget> {
String? _selectedCategory;
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
children: [
const SizedBox(height: 18,),
const Padding(
padding: EdgeInsets.all(8.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'Stores For You',
style: TextStyle(
fontWeight: FontWeight.bold,
letterSpacing: 1,
fontSize: 20
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(8,0,8,8),
child: SizedBox(
height: 40,
child: Row(
children: [
Expanded(
child:FirestoreListView<Category>(
scrollDirection: Axis.horizontal,
query: categoryCollection,
itemBuilder: (context, snapshot) {
Category category = snapshot.data();
return Padding(
padding: const EdgeInsets.only(right: 4),
child: ActionChip(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(2)
),
backgroundColor: _selectedCategory == category.catName ? Colors.blue.shade900 : Colors.grey,
label: Text(
category.catName!,
style: TextStyle(
fontSize: 12,
color: _selectedCategory==category.catName ? Colors.white : Colors.black
),
),
onPressed: () {
setState(() {
_selectedCategory = category.catName;
});
},
),
);
},
),
),
Container(
decoration: BoxDecoration(
border: Border(left: BorderSide(color: Colors.grey.shade400),)
),
child: IconButton(
onPressed: (){
},
icon: const Icon(IconlyLight.arrowDown),
),
)
],
),
),
),
],
),
);
}
}
main category widget
import 'package:buyfast/Widget/category/sub_category_widget.dart';
import 'package:buyfast/models/main_category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
class MainCategoryWidget extends StatefulWidget {
final String? selectedCat;
const MainCategoryWidget({this.selectedCat,Key? key}) : super(key: key);
#override
State<MainCategoryWidget> createState() => _MainCategoryWidgetState();
}
class _MainCategoryWidgetState extends State<MainCategoryWidget> {
#override
Widget build(BuildContext context) {
return Expanded(
child: FirestoreListView<MainCategory>(
query: mainCategoryCollection(widget.selectedCat),
itemBuilder: (context, snapshot) {
MainCategory mainCategory = snapshot.data();
return ExpansionTile(
title: Text(mainCategory.mainCategory!),
children: [
SubCategoryWidget(
selectedSubCat: mainCategory.mainCategory,
)
],
);
},
),
);
}
}
main category model
import 'package:buyfast/firebase_service.dart';
class MainCategory {
MainCategory({this.category, this.mainCategory});
MainCategory.fromJson(Map<String, Object?> json)
: this(
category: json['category']! as String,
mainCategory: json['mainCategory']! as String,
);
final String? category;
final String? mainCategory;
Map<String, Object?> toJson() {
return {
'category': category,
'mainCategory': mainCategory,
};
}
}
FirebaseService _service = FirebaseService();
mainCategoryCollection (selectedCat){
return _service.mainCategories.where('approved',isEqualTo: true).where('category', isEqualTo: selectedCat).withConverter<MainCategory>(
fromFirestore: (snapshot, _) => MainCategory.fromJson(snapshot.data()!),
toFirestore: (category, _) => category.toJson(),);
}
`
Subcategory model
`
import 'package:buyfast/firebase_service.dart';
class SubCategory {
SubCategory({this.mainCategory, this.subCatName, this.image});
SubCategory.fromJson(Map<String, Object?> json)
: this(
mainCategory: json['mainCategory']! as String,
subCatName: json['subCatName']! as String,
image: json['image']! as String,
);
final String? mainCategory;
final String? subCatName;
final String? image;
Map<String, Object?> toJson() {
return {
'mainCategory': mainCategory,
'subCatName': subCatName,
'image': image,
};
}
}
FirebaseService _service = FirebaseService();
subCategoryCollection({selectedSubCat}){
return _service.subCategories.where('active',isEqualTo: true).where('mainCategory',isEqualTo: selectedSubCat).withConverter<SubCategory>(
fromFirestore: (snapshot, _) => SubCategory.fromJson(snapshot.data()!),
toFirestore: (category, _) => category.toJson(),
);
}
`
Subcategory widget
`
import 'package:buyfast/models/sub_category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
class SubCategoryWidget extends StatelessWidget {
final String? selectedSubCat;
const SubCategoryWidget({this.selectedSubCat,Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Expanded(
child: FirestoreQueryBuilder<SubCategory>(
query: subCategoryCollection(
selectedSubCat: selectedSubCat
),
builder: (context, snapshot, _) {
if (snapshot.isFetching) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Text('Something went wrong! ${snapshot.error}');
}
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: snapshot.docs.length == 0 ? 1/.1 : 1/1.1,
),
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
SubCategory subCat = snapshot.docs[index].data();
return InkWell(
onTap: (){
//move to product screen
},
child: Column(
children: [
SizedBox(
height: 60,
width: 60,
child: FittedBox(
fit: BoxFit.contain,
child: Image.network(subCat.image!)),
),
Text(subCat.subCatName!,style: const TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
],
),
);
},
);
},
),
);
}
}
`
I solved it by wrapping the Expanded widget in the sub_category_widget.dart file with a SizeBox and give it a height of 100.
Like below.
import 'package:buyfast/models/sub_category_model.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:flutter/material.dart';
class SubCategoryWidget extends StatelessWidget {
final String? selectedSubCat;
const SubCategoryWidget({this.selectedSubCat,Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SizeBox(height: 100,
Expanded(
child: FirestoreQueryBuilder<SubCategory>(
query: subCategoryCollection(
selectedSubCat: selectedSubCat
),
builder: (context, snapshot, _) {
if (snapshot.isFetching) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Text('Something went wrong! ${snapshot.error}');
}
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: snapshot.docs.length == 0 ? 1/.1 : 1/1.1,
),
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
SubCategory subCat = snapshot.docs[index].data();
return InkWell(
onTap: (){
//move to product screen
},
child: Column(
children: [
SizedBox(
height: 60,
width: 60,
child: FittedBox(
fit: BoxFit.contain,
child: Image.network(subCat.image!)),
),
Text(subCat.subCatName!,style: const TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
],
),
);
},
);
},
),
),
);
}
}
I have created this video player for my application which can play video from assets. Since, It is made from (video_player) package I guess I can play only one video with it But I want 3-4 videos to be played. How can I do that? It is possible or not...Help me! Furthermore, I also want to make the video the option of 10 seconds backward and forward while pressing it's sides. Thanks for your help!
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
void main() {
runApp(VideoPlay());
}
class VideoPlay extends StatefulWidget {
String? pathh;
#override
_VideoPlayState createState() => _VideoPlayState();
VideoPlay({
this.pathh = "assets/video.mp4", // Video from assets folder
});
}
class _VideoPlayState extends State<VideoPlay> {
late VideoPlayerController controller;
late Future<void> futureController;
#override
void initState() {
//url to load network
controller = VideoPlayerController.asset(widget.pathh!);
futureController = controller.initialize();
controller.setLooping(true);
controller.setVolume(25.0);
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FutureBuilder(
future: futureController,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: VideoPlayer(controller));
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
),
),
Padding(
padding: const EdgeInsets.all(6.0),
child: RaisedButton(
color: Color(0xff9142db),
child: Icon(
controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
),
onPressed: () {
setState(() {
if (controller.value.isPlaying) {
controller.pause();
} else {
controller.play();
}
});
},
),
)
],
));
}
}
App Image Is Here
I like your idea and wanted to deal with it, this is the result.
I hope you can do better.
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
void main(List<String> args) {
runApp(Example());
}
class Example extends StatelessWidget {
const Example({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
color: Colors.white,
debugShowCheckedModeBanner: false,
home: VideoPlayersList(),
);
}
}
class VideoPlayersList extends StatelessWidget {
const VideoPlayersList({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
List<String> paths = [
"assets/images/testvideo.mp4",
"assets/images/testvideo.mp4",
"assets/images/testvideo.mp4",
"assets/images/testvideo.mp4",
"assets/images/testvideo.mp4",
];
return Scaffold(
body: SingleChildScrollView(
child: Column(children: [
ListView.builder(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemCount: paths.length,
itemBuilder: (BuildContext context, int index) {
return VideoPlay(
pathh: paths[index],
);
},
),
]),
),
);
}
}
class VideoPlay extends StatefulWidget {
String? pathh;
#override
_VideoPlayState createState() => _VideoPlayState();
VideoPlay({
Key? key,
this.pathh, // Video from assets folder
}) : super(key: key);
}
class _VideoPlayState extends State<VideoPlay> {
ValueNotifier<VideoPlayerValue?> currentPosition = ValueNotifier(null);
VideoPlayerController? controller;
late Future<void> futureController;
initVideo() {
controller = VideoPlayerController.asset(widget.pathh!);
futureController = controller!.initialize();
}
#override
void initState() {
initVideo();
controller!.addListener(() {
if (controller!.value.isInitialized) {
currentPosition.value = controller!.value;
}
});
super.initState();
}
#override
void dispose() {
controller!.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: futureController,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator.adaptive();
} else {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: SizedBox(
height: controller!.value.size.height,
width: double.infinity,
child: AspectRatio(
aspectRatio: controller!.value.aspectRatio,
child: Stack(children: [
Positioned.fill(
child: Container(
foregroundDecoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black.withOpacity(.7),
Colors.transparent
],
stops: [
0,
.3
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter),
),
child: VideoPlayer(controller!))),
Positioned.fill(
child: Column(
children: [
Expanded(
flex: 8,
child: Row(
children: [
Expanded(
flex: 3,
child: GestureDetector(
onDoubleTap: () async {
Duration? position =
await controller!.position;
setState(() {
controller!.seekTo(Duration(
seconds: position!.inSeconds - 10));
});
},
child: const Icon(
Icons.fast_rewind_rounded,
color: Colors.black,
size: 40,
),
),
),
Expanded(
flex: 4,
child: IconButton(
icon: Icon(
controller!.value.isPlaying
? Icons.pause
: Icons.play_arrow,
color: Colors.black,
size: 40,
),
onPressed: () {
setState(() {
if (controller!.value.isPlaying) {
controller!.pause();
} else {
controller!.play();
}
});
},
)),
Expanded(
flex: 3,
child: GestureDetector(
onDoubleTap: () async {
Duration? position =
await controller!.position;
setState(() {
controller!.seekTo(Duration(
seconds: position!.inSeconds + 10));
});
},
child: const Icon(
Icons.fast_forward_rounded,
color: Colors.black,
size: 40,
),
),
),
],
),
),
Expanded(
flex: 2,
child: Align(
alignment: Alignment.bottomCenter,
child: ValueListenableBuilder(
valueListenable: currentPosition,
builder: (context,
VideoPlayerValue? videoPlayerValue, w) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
child: Row(
children: [
Text(
videoPlayerValue!.position
.toString()
.substring(
videoPlayerValue.position
.toString()
.indexOf(':') +
1,
videoPlayerValue.position
.toString()
.indexOf('.')),
style: const TextStyle(
color: Colors.white,
fontSize: 22),
),
const Spacer(),
Text(
videoPlayerValue.duration
.toString()
.substring(
videoPlayerValue.duration
.toString()
.indexOf(':') +
1,
videoPlayerValue.duration
.toString()
.indexOf('.')),
style: const TextStyle(
color: Colors.white,
fontSize: 22),
),
],
),
);
}),
))
],
),
),
])),
),
);
}
},
);
}
}
I'm trying to fetch data from Genshin API, code below is working, but only with delay (in GenshinCubit class), it looks weard, because I don't know how much time to set for delay. I think, there is a problem in code, cause it must not set the GenshinLoaded state before the loadedList is completed. Now, if I remove the delay, it just sets the GenshinLoaded when the list is still in work and not completed, await doesn't help. Because of that I get a white screen and need to hot reload for my list to display.
class Repository {
final String characters = 'https://api.genshin.dev/characters/';
Future<List<Character>> getCharactersList() async {
List<Character> charactersList = [];
List<String> links = [];
final response = await http.get(Uri.parse(characters));```
List<dynamic> json = jsonDecode(response.body);
json.forEach((element) {
links.add('$characters$element');
});
links.forEach((element) async {
final response2 = await http.get(Uri.parse(element));
dynamic json2 = jsonDecode(response2.body);
charactersList.add(Character.fromJson(json2));
});
return charactersList;
}
}
class GenshinCubit extends Cubit<GenshinState> {
final Repository repository;
GenshinCubit(this.repository) : super(GenshinInitial(),);
getCharacters() async {
try {
emit(GenshinLoading());
List<Character> list = await repository.getCharactersList();
await Future<void>.delayed(const Duration(milliseconds: 1000));
emit(GenshinLoaded(loadedList: list));
}catch (e) {
print(e);
emit(GenshinError());
}
}
}
class HomeScreen extends StatelessWidget {
final userRepository = Repository();
HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider<GenshinCubit>(
create: (context) => GenshinCubit(userRepository)..getCharacters(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(body: Container(child: const CharactersScreen())),
),
);
}
}
class CharactersScreen extends StatefulWidget {
const CharactersScreen({
Key? key,
}) : super(key: key);
#override
State<CharactersScreen> createState() => _CharactersScreenState();
}
class _CharactersScreenState extends State<CharactersScreen> {
#override
Widget build(BuildContext context) {
return Column(
children: [
BlocBuilder<GenshinCubit, GenshinState>(
builder: (context, state) {
if (state is GenshinLoading) {
return Center(
child: CircularProgressIndicator(),
);
}
if (state is GenshinLoaded) {
return SafeArea(
top: false,
child: Column(
children: [
Container(
color: Colors.black,
height: MediaQuery.of(context).size.height,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: state.loadedList.length,
itemBuilder: ((context, index) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 50.0, horizontal: 50),
child: GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CharacterDetailsPage(
character: state.loadedList[index],
),
),
),
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.blueAccent.withOpacity(0.3),
borderRadius: const BorderRadius.all(
Radius.circular(
30,
),
)),
child: Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(
right: 30.0, bottom: 30),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
state.loadedList[index].name
.toString(),
style: TextStyle(
color: Colors.black,
fontSize: 50),
),
RatingBarIndicator(
itemPadding: EdgeInsets.zero,
rating: double.parse(
state.loadedList[index].rarity
.toString(),
),
itemCount: int.parse(
state.loadedList[index].rarity
.toString(),
),
itemBuilder: (context, index) =>
Icon(
Icons.star_rate_rounded,
color: Colors.amber,
))
],
),
),
),
),
),
);
})),
),
],
),
);
}
if (state is GenshinInitial) {
return Text('Start');
}
if (state is GenshinError) {
return Text('Error');
}
return Text('Meow');
}),
],
);
}
}
I found a solution!
I've got that problem because of forEach. How to wait for forEach to complete with asynchronous callbacks? - there is a solution.
Why I am not able to use setState Under GestureDetector Using onTap:
After I use setState I got an error like: The function 'setState' isn't defined.
Try importing the library that defines 'setState', And VS Code editor show me
Error like: correcting the name to the name of an existing function, or defining a function named 'setState'.dart(undefined_function).
I try to fix different ways please tell me where is my problem
Thank you
Some Flutter Import Link ::::::::::
class ParsingMap extends StatefulWidget {
const ParsingMap({Key? key}) : super(key: key);
#override
_ParsingMapState createState() => _ParsingMapState();
}
class _ParsingMapState extends State<ParsingMap> {
Future<ApiList>? data;
#override
void initState() {
super.initState();
Network network = Network("https://fakestoreapi.com/products");
data = network.loadPosts();
// print(data);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: Icon(Icons.arrow_left, color: Colors.black),
actions: [
Icon(Icons.search, color: Colors.black),
SizedBox(width: 10),
Icon(Icons.home_filled, color: Colors.black),
SizedBox(width: 8)
],
),
body: Center(
child: Container(
child: FutureBuilder(
future: data,
builder: (context, AsyncSnapshot<ApiList> snapshot) {
List<Api> allPosts;
if (snapshot.hasData) {
allPosts = snapshot.data!.apis!;
return createListView(allPosts, context);
}
return CircularProgressIndicator();
},
),
),
),
);
}
}
class Network {
final String url;
Network(this.url);
Future<ApiList> loadPosts() async {
final response = await get(Uri.parse(url));
if (response.statusCode == 200) {
// print(response.body);
return ApiList.fromJson(json.decode(response.body));
} else {
throw Exception("Faild To get posts");
}
}
}
Widget createListView(List<Api> data, BuildContext context) {
return ListView(
children: [
Container(
height: 300,
margin: EdgeInsets.symmetric(vertical: 16),
child: ListView.builder(
itemCount: data.length,
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 16),
itemBuilder: (context, index) {
int selectedIndex = 0;
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index;
});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${data[index].category}",
style: TextStyle(
fontWeight: FontWeight.bold,
color: selectedIndex == index
? Colors.black
: Colors.black38),
),
Container(
margin: EdgeInsets.only(
top: 5,
),
height: 2,
width: 30,
color: selectedIndex == index
? Colors.black
: Colors.transparent,
)
],
),
),
);
},
),
),
],
);
}
Put top-level createListView function in _ParsingMapState class.
class _ParsingMapState extends State<ParsingMap> {
// ...
Widget createListView(...) {}
}
I have the following Flutter BottomNavigationBar, I have 3 children page widgets. As soon as the user writes a review and press the submit button, I am calling the void _clear() which resets the values in the textfield widgets. This method is inside and called from the WriteReview widget but is not working, the screen is not refreshing. The data it self is being reset but the UI has not be refreshed. I tried with and without setState(){ _clear()}; but no results.
I have similar issue with the TabBarView. What I would expect as a result would be the selected widget page to be refreshed.
I assume is something different on how the widgets are being handled in the TabBarView and BottomNavigationBar that I am missing.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HomeView extends StatefulWidget {
#override
_HomeViewState createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
final String _writeReviewLabel = 'Write review';
final String _searchLabel = 'Search';
final String _accountLabel = 'Account';
var _currentIndex = 0;
final List<Widget> _bottomBarItems = [
WriteReviewView(),
SearchView(),
UserAccountView()
];
#override
Widget build(BuildContext context) {
final accentColor = Theme.of(context).accentColor;
final primaryColor = Theme.of(context).primaryColor;
return BaseView<HomePresenter>(
createPresenter: () => HomePresenter(),
onInitialize: (presenter) {
_currentIndex = ModalRoute.of(context).settings.arguments;
},
builder: (context, child, presenter) => Scaffold(
bottomNavigationBar: BottomNavigationBar(
backgroundColor: primaryColor,
selectedItemColor: accentColor,
unselectedItemColor: Colors.white,
elevation: 0,
currentIndex: _currentIndex,
onTap: (index) {
onTabTapped(index, presenter);
},
items: [
BottomNavigationBarItem(
activeIcon: Icon(Icons.rate_review),
icon: Icon(Icons.rate_review),
title: Text(_writeReviewLabel),
),
BottomNavigationBarItem(
activeIcon: Icon(Icons.search),
icon: Icon(Icons.search),
title: Text(_searchLabel),
),
BottomNavigationBarItem(
activeIcon: Icon(Icons.person),
icon: Icon(Icons.person),
title: Text(_accountLabel)),
],
),
body: _bottomBarItems[_currentIndex]),
);
}
void onTabTapped(int index, HomePresenter presenter) {
if (index == _currentIndex) {
return;
}
if (presenter.isAuthenticated) {
setState(() {
_currentIndex = index;
});
} else {
presenter.pushNamed(context, Routes.login, arguments: () {
if (presenter.isAuthenticated) {
Future.delayed(const Duration(microseconds: 500), () {
setState(() {
_currentIndex = index;
});
});
}
});
}
}
}
Write Review View
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'base/BaseView.dart';
class WriteReviewView extends StatefulWidget {
#override
_WriteReviewViewState createState() => _WriteReviewViewState();
}
class _WriteReviewViewState extends State<WriteReviewView> {
final String _locationLabel = 'Location';
final String _reviewLabel = 'Review';
#override
Widget build(BuildContext context) {
return BaseView<WriteReviewPresenter>(
createPresenter: () => WriteReviewPresenter(),
onInitialize: (presenter) {
presenter.init();
},
builder: (context, child, presenter) => Container(
color: Theme.of(context).backgroundColor,
child: _body(presenter),
));
}
Widget _body(WriteReviewPresenter presenter) {
final height = MediaQuery.of(context).size.height;
return LayoutBuilder(builder: (context, constraint) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraint.maxHeight),
child: IntrinsicHeight(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
margin: EdgeInsets.fromLTRB(4.0, 4.0, 4.0, 8.0),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10, bottom: 20),
child: Row(
children: [
Icon(Icons.location_on,
color: Theme.of(context).cursorColor),
SectionTitle(_locationLabel),
],
),
),
ChooseAddressButton(presenter.addressContainer.address, () {
presenter.navigateNewAddress(context);
}),
SizedBoxes.medium,
],
),
),
),
Card(
margin: EdgeInsets.fromLTRB(4.0, 4.0, 4.0, 8.0),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(top: 10),
child: Row(
children: [
Icon(Icons.star, color: Theme.of(context).indicatorColor),
SectionTitle(_reviewLabel),
],
),
),
SizedBoxes.medium,
CustomTextFormField(
data: presenter.reviewContainer.review,
showMinimum: true,
characterLimit: 50,
maxLines: 4,
height: 100),
ModifyRatingBar(50.0, presenter.reviewContainer.rating, false),
Padding(
padding: EdgeInsets.symmetric(vertical: 5),
child: Divider(indent: 40, endIndent: 40)),
],
),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Card(
color: Theme.of(context).accentColor,
child: FlatButton(
onPressed: () {
_submit(context, presenter);
},
child: Text('Submit',style:TextStyle(fontWeight: FontWeight.bold,fontSize: 18,color: Colors.white)),
),
),
),
],
),
),
),
);
});
}
void _submit(BuildContext context, WriteReviewPresenter presenter) async {
LoadingPopup.show(context);
final status = await presenter.submit(context);
LoadingPopup.hide(context);
if (status == ReviewStatus.successful) {
PostCompletePopup.show(context);
setState(() {
presenter.clear();
});
} else {
presenter.showError(context, status);
}
}
}
setState() only refreshes the widget that called it, and all the widgets under it.
When calling setState()from WriteReview widget, it will not update the HomeView widget. Try moving the setState()call to the HomeView widget using some sort of callback.