Flutter Listview not scrolling to down by programatically - flutter

I have a simple chat app I need to scroll to bottom of list view when data loads but its not going to bottom.
My code
final ScrollController _scrollController = ScrollController();
void connectsocket() {
userId = Provider.of<UserController>(context, listen: false)
.userdata
.sId
.toString();
try {
socket = IO.io(ApiUrl.chatUrl, <String, dynamic>{
'transports': ['websocket'],
'autoConnect': true,
'force new connection': true,
});
socket.connect();
socket.on('connect', (_) {
print('connect: ${socket.id}');
socket.emit('joinRoom', {'user': userId, 'chatroom': widget.roomid});
socket.on('getRoom', (data) {
setState(() {
messageList = data['data']['messages'];
});
Future.delayed(Duration(seconds: 1), () {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 500),
);
});
});
socket.on('message', (data) {
setState(() {
messageList.add(data['data']);
messageList = Set.of(messageList).toList();
});
});
});
} catch (e) {
print(e.toString());
}
}
ListView.
SizedBox(
width: size.width * 0.95,
child: ListView.builder(
controller: _scrollController,
shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
itemCount: messageList.length,
itemBuilder: (context, index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Card(
color: Theme.of(context).colorScheme.tertiary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)),
),
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(13.0),
child: Text(
messageList[index]['message'],
style: Theme.of(context).textTheme.bodyMedium,
),
),
),
],
);
}),
),
When I get the message list I simply set data in a list and make the controller go down after 1 second but it's not scrolling down. I try to set the height etc but nothing is working.

Related

Store Image in List<Xfile> from image urls

I have created a variable
List<Xfile> imageList;
using this variable I have showed the selected images in GridView.Builder and uploaded them.
But I want to store those uploaded images in this List to show them again in GridView.Builder.
Means How to store images from imageUrls in List
How can I achieve this?
Follow as follows:
Variables
final picker = ImagePicker();
File? file;
XFile? pickedImage;
bool isLoading = false;
List<File?> fileList = [];
Method to select image from gallery
Future pickImageFromGallery() async {
pickedImage = await picker.pickImage(source: ImageSource.gallery);
setState(() {
file = File(pickedImage!.path);
fileList.add(file);
});
}
And place in gridview as follows:
GridView.builder(
itemCount: fileList.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (BuildContext context, int i) {
return Container(
padding: const EdgeInsets.all(10),
child: Stack(
children: <Widget>[
SizedBox(
height: 100,
width: 100,
child: Image.file(File(fileList[i]!.path),fit: BoxFit.cover,),
),
Positioned(
right: 1,
child: GestureDetector(
onTap: () {
setState(() {
dltImages(fileList[i]);
});
},
child: const Icon(Icons.cancel, color: Colors.red),
))
],
),
);
},
),
Find full code at:
https://github.com/nbnD/image_picker_flutter/blob/master/lib/homepage.dart
I do like this if there is multi images upload
class PickImagesPage extends StatefulWidget {
const PickImagesPage({super.key, required this.initialUrls});
final List<String> initialUrls;
#override
State<PickImagesPage> createState() => _PickImagesPageState();
}
class _PickImagesPageState extends State<PickImagesPage> {
#override
void initState() {
urls = widget.initialUrls;
super.initState();
}
List<String> urls = [];
List<File> files = [];
List<String> removedUrls = [];
final Repo repo = Repo();
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final style = theme.textTheme;
final scheme = theme.colorScheme;
return LoadingLayer(
child: Scaffold(
bottomNavigationBar: Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
child: ElevatedButton(
onPressed:
files.isNotEmpty || widget.initialUrls.length != urls.length
? () async {
try {
await repo.uploadImages(
files: files,
urls: urls,
removedUrls: removedUrls,
);
Navigator.pop(context);
} catch (e) {
AppSnackbar(context).error(e);
if (kDebugMode) {
print(e);
}
}
}
: null,
child: const Text(Labels.save),
),
),
appBar: AppBar(
title: const Text(
Labels.ambienceImages,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final List<XFile> pickedFiles = await pickImages();
if (pickedFiles.isNotEmpty) {
setState(() {
files.addAll(pickedFiles.map((e) => File(e.path)));
});
}
},
child: const Icon(Icons.add),
),
body: GridView.count(
padding: const EdgeInsets.all(12),
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
children: [
...urls
.map(
(e) => GestureDetector(
onTap: () {
setState(() {
urls.remove(e);
removedUrls.add(e);
});
},
child: Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: scheme.surfaceVariant.withOpacity(0.5),
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: NetworkImage(e),
),
),
),
),
)
.toList(),
...files
.map(
(e) => Container(
clipBehavior: Clip.antiAlias,
alignment: Alignment.topRight,
decoration: BoxDecoration(
color: scheme.surfaceVariant.withOpacity(0.5),
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: FileImage(e),
),
),
child: SizedBox(
height: 40,
width: 40,
child: RawMaterialButton(
elevation: 0,
focusElevation: 0,
hoverElevation: 0,
shape: const CircleBorder(),
fillColor: theme.cardColor.withOpacity(0.5),
onPressed: () {
setState(() {
files.remove(e);
});
},
child: const Icon(Icons.remove),
),
),
),
)
.toList(),
GestureDetector(
onTap: () async {
final List<XFile> pickedFiles = await pickImages();
if (pickedFiles.isNotEmpty) {
setState(() {
files.addAll(pickedFiles.map((e) => File(e.path)));
});
}
},
child: Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: scheme.surfaceVariant.withOpacity(0.5),
borderRadius: BorderRadius.circular(20),
),
child: Stack(
children: const [
Center(
child: Icon(Icons.add),
),
PickImageLabel(),
],
),
),
),
],
),
),
);
}
}
class Repo {
Future<void> uploadImages(
{required List<String> urls,
required List<File> files,
required List<String> removedUrls}) async {
List<String> newUrls = [];
for (var file in files) {
final url = await upload(file);
newUrls.add(url);
}
for (var url in removedUrls) {
await deleteImage(url);
}
await saveImages(urls + newUrls);
}
}

Listview.Builder displays the the widget more than one time

I have two questions I hope you can help me with. I am building a listview.builder to loop through my array of objects and when I loop and display it displays the widget more than one time(photo is provided)
I am trying to iterate through my prefs of products to get the price of the product and quantity and add them to display the overall price, I tried forEach method but I couldn't quite figure out how.
Thanks for you help
class CheckOutCart extends StatefulWidget {
const CheckOutCart({Key? key}) : super(key: key);
#override
State<CheckOutCart> createState() => _CheckOutCartState();
}
class _CheckOutCartState extends State<CheckOutCart> {
late SharedPreferences sharedPrefs;
List<String>? cart;
// List<List<String ,int>> productsWithQuantity;
List productsWithQuantity = [];
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getPrefs(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// return Text(productsWithQuantity[0][1]);
return buildContainer(productsWithQuantity);
}
return Center(
child: CircularProgressIndicator()); // or some other widget
},
);
}
Future<void> _getPrefs() async {
sharedPrefs = await SharedPreferences.getInstance();
cart = sharedPrefs.getStringList('userCart');
getProductsAsObj(cart);
}
Container buildContainer(productsWithQuantity) {
final Random random = new Random(5);
return Container(
padding: EdgeInsets.symmetric(
vertical: getProportionateScreenWidth(15),
horizontal: getProportionateScreenWidth(30),
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30), topRight: Radius.circular(30)),
boxShadow: [
BoxShadow(
offset: Offset(0, -15),
blurRadius: 20,
color: Color(0XFFDADADA).withOpacity(0.15),
),
],
),
child: SafeArea(
child: ListView.builder(
shrinkWrap: true,
itemCount: productsWithQuantity.length, //length of cart
itemBuilder: (context, index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TotalPriceField(
titleText: "Merchandise Subtotal:\n",
priceText: productsWithQuantity[index][0].retail_price.toString(),
// priceText: productsWithQuantity.forEach((cart) => cart += cart[index][0].retail_price).toString()
// "\$375.5",
),
TotalPriceField(
titleText: "Shipping Total:\n",
priceText: "${random.nextInt(5)}",
),
],
),
SizedBox(
height: getProportionateScreenHeight(20),
),
TotalPriceField(
titleText: "Total Payment:\n",
priceText: "380.5",
),
SizedBox(
height: getProportionateScreenHeight(15),
),
SizedBox(
width: getProportionateScreenWidth(290),
child: DefaultButton(
text: "Check Out",
press: () {},
),
),
],
),
),
),
);
}
void getProductsAsObj(cart) {
for (var i = 0; i < cart.length; i++) {
var item = cart[i];
var items = item.split('-quantity-');
var product_ = items[0];
var quantity_ = items[1];
print(quantity_);
// product_ = '['+product_+']';
Map<String, dynamic> valueMap = json.decode(product_);
var product_obj = Product.fromMap(valueMap);
var itemx = [product_obj, quantity_];
productsWithQuantity.add(itemx);
}
}
}
```[![Listview.builder][1]][1]
[1]: https://i.stack.imgur.com/4wOyi.png

Empty container consuming space in gridview builder when searching through a list

Code :
class MobileCourses extends StatefulWidget { const MobileCourses({ Key? key }) : super(key: key);
#override _MobileCoursesState createState() =>
_MobileCoursesState(); }
class _MobileCoursesState extends State<MobileCourses> { String searchCourse = ""; TextEditingController searchController = TextEditingController(); #override Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
searchBar(),
Expanded(
child: Container(
padding: EdgeInsets.all(15),
child : FutureBuilder(
future: Networking.getAllCourses(),
builder: (_,snapshot)
{
if(snapshot.hasData)
{
List<SpecificCourse>? specificCourse = snapshot.data as List<SpecificCourse>?;
return GridView.builder(
physics: BouncingScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 15,
crossAxisSpacing: 15
),
itemCount: specificCourse![0].courses.length ,
itemBuilder: (_,index)
{
return specificCourse[0].courses[index].course.toLowerCase().contains(searchCourse) ? MobileContainer(course: specificCourse[0].courses[index].course):Container(color: Colors.red,),
// over here i am searching for my required course
},
);
}
else
{
return CircularProgressIndicator();
}
}
),
),
),
],
),
);
}
}
Search Bar :-
Widget searchBar() {
return Padding(
padding: const EdgeInsets.only(left : 15.0,right: 15.0 , top: 10.0 ,bottom: 10.0),
child: Container(
child: TextField(
decoration: InputDecoration(
hintText: "Search...",
prefixIcon: Icon(Icons.search)
),
onChanged: (value){
setState(() {
searchCourse = value.toLowerCase();
});
},
controller: searchController,
),
),
);
}
I want to implement search function inside my gridview and i have tried to implement it in the above code and the ui is looking something like this
but when i search "m" it should return me only MBA but it is returning container too i do not want these container(colored in red) i only want MBA .......I have explicity given container red for better understanding
Container without red color
i only want to return MBA but the empty container is consuming space. Please help ! and sorry for these big images i do not know how to decrease their size
Try to below code its working for me for ListView.Builder Please try to change it Gridview hope it helps you:
Create one class with your JSON Data :
class User {
String name;
int id;
int totalLeads;
User({
this.id,
this.name,
this.totalLeads,
});
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
id: int.parse(json['userId']),
totalLeads: int.parse(json['total']),
);
}
}
Create one class that you declare voidCallback function:
class Debouncer {
final int milliseconds;
VoidCallback action;
Timer _timer;
Debouncer({this.milliseconds});
run(VoidCallback action) {
if (null != _timer) {
_timer.cancel();
}
_timer = Timer(Duration(milliseconds: milliseconds), action);
}
}
Create Stateful Widget Class
class AllAgents extends StatefulWidget {
AllAgents() : super();
#override
AllAgentsState createState() => AllAgentsState();
}
class AllAgentsState extends State<AllAgents> {
final _debouncer = Debouncer(milliseconds: 500);
List<User> users = [];
List<User> filteredUsers = [];
static String url = 'your API url here';
static Future<List<User>> getAllLeagentsList() async {
try {
final response = await http.get(url);
if (response.statusCode == 200) {
List<User> list = parseAgents(response.body);
return list;
} else {
throw Exception('Error');
}
} catch (e) {
throw Exception(e.toString());
}
}
static List<User> parseAgents(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<User>((json) => User.fromJson(json)).toList();
}
#override
void initState() {
super.initState();
getAllLeagentsList().then((usersFromServer) {
setState(() {
users = usersFromServer;
filteredUsers = users;
});
});
}
// Declare Your Widget here
Column(children:[
//Search Bar to List of typed User
Container(
padding: EdgeInsets.only(top: 20, left: 20, right: 20),
child: TextField(
textInputAction: TextInputAction.next,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.0),
borderSide: BorderSide(
color: Colors.grey,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(20.0),
borderSide: BorderSide(
color: Colors.blue,
),
),
suffixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: () {},
),
contentPadding: EdgeInsets.all(15.0),
hintText: 'Search ',
),
onChanged: (string) {
_debouncer.run(() {
setState(() {
filteredUsers = users
.where((u) => (u.name
.toLowerCase()
.contains(string.toLowerCase())))
.toList();
});
});
},
),
),
//Lists of Agents
Expanded(
child: ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
padding: EdgeInsets.only(top: 20, left: 20, right: 20),
itemCount: filteredUsers.length,
itemBuilder: (BuildContext context, int index) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side: BorderSide(
color: Colors.grey[300],
),
),
child: Padding(
padding: EdgeInsets.all(5.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListTile(
leading: Container(
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.white,
border: Border.all(color: Colors.blue),
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 2.0,
spreadRadius: 1.0,
offset: Offset(
1.0,
1.0,
), // shadow direction: bottom right
)
],
),
child: Text(filteredUsers[index].name[0]),
),
title: Text(
filteredUsers[index].name,
style: TextStyle(fontSize: 16),
),
)
],
),
),
);
},),),
],),
}
here is my screen without search ->
here is my screen with search ->

Facing issues while making chat real time using apis flutter

Hi Im developing chat app in flutter having sql backend. Web developers have provide me apis for sending and receiveing messages. The app is working fine, the issue is its not so real time, like it take time to send message and after that i have to manually call set state to get new message. So is there any way to make chat real using apis in flutter?
I am writing following code to implement this:
Widget _getUI(BuildContext context) {
return FutureProvider.value(
value: _services.getUserMessages(
context, widget.chatUserList.chatConnectionID),
builder: (context, child) {
print("Called");
return Container(
child: context.watch<List<MessagesModel>>() == null
? LoadingWidget()
: Column(
children: [
Expanded(
child: Container(
child: ListView.builder(
controller: _scrollController,
itemCount:
context.watch<List<MessagesModel>>().length,
itemBuilder: (context, i) {
Timer(
Duration(milliseconds: 300),
() => _scrollController.animateTo(
_scrollController
.position.maxScrollExtent,
duration: Duration(milliseconds: 700),
curve: Curves.ease));
return Padding(
padding:
const EdgeInsets.symmetric(vertical: 2.0),
child: MessageTile(
message: context
.watch<List<MessagesModel>>()[i]
.textMessage,
sendByMe: context
.watch<List<MessagesModel>>()[i]
.chatReceiverTypeId !=
userModel.patientID,
time: context
.watch<List<MessagesModel>>()[i]
.createdDate,
),
);
}),
),
),
Container(
alignment: Alignment.bottomCenter,
width: MediaQuery.of(context).size.width,
color: Colors.white,
child: Container(
padding:
EdgeInsets.symmetric(horizontal: 24, vertical: 10),
child: Row(
children: [
Expanded(
child: TextField(
style:
TextStyle(color: Colors.black, fontSize: 13),
controller: messageController,
onChanged: (val) {
setState(() {});
},
decoration: InputDecoration(
hintText: "Type Here...",
hintStyle: TextStyle(
color: Colors.black,
fontSize: 13,
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide.none),
border: OutlineInputBorder(
borderSide: BorderSide.none)),
)),
SizedBox(
width: 16,
),
IconButton(
onPressed: () async {
if (messageController.text.isEmpty) {
return;
}
Timer(
Duration(milliseconds: 300),
() => _scrollController.animateTo(
_scrollController
.position.maxScrollExtent,
duration: Duration(milliseconds: 700),
curve: Curves.ease));
await _services.createMessage(context,
model: MessageComposerModel(
senderId: userModel.patientID,
senderTypeId: 2,
message: messageController.text,
receiverId:
widget.chatUserList.doctorID,
receiverTypeId: 1,
chatConnectionId: widget
.chatUserList.chatConnectionID,
lastInsertedMessageID: 0));
if (Provider.of<AppState>(context,
listen: false)
.getStateStatus() ==
AppCurrentState.IsFree) {
messageController.clear();
}
setState(() {});
},
icon: Icon(
Icons.send,
color: messageController.text.isEmpty
? Colors.grey
: Colors.black,
),
)
],
),
),
)
],
),
);
},
);
}
And my services call look like this:
Future<void> createMessage(BuildContext context,
{MessageComposerModel model, String authToken}) async {
try {
return await http.post(
Uri.parse(BackendConfigs.apiBaseUrl + "/api/Chat/SendChatMessage"),
body: json.encode(model.toJson()),
headers: {
'Authorization': 'Bearer $authToken',
'Content-Type': 'application/json'
},
).then((response) {
if (response.statusCode == 201 || response.statusCode == 200) {
Provider.of<AppState>(context, listen: false)
.stateStatus(AppCurrentState.IsFree);
print(response.body);
// return SuccessResponseModel.fromJson(json.decode(response.body));
} else {
Provider.of<AppState>(context, listen: false)
.stateStatus(AppCurrentState.IsFree);
Provider.of<ErrorString>(context, listen: false)
.saveErrorString(response.reasonPhrase);
}
});
} on HttpException catch (e) {
Provider.of<AppState>(context, listen: false)
.stateStatus(AppCurrentState.IsError);
Provider.of<ErrorString>(context, listen: false)
.saveErrorString(e.message);
rethrow;
}
}

change story items as dynamic widgets in flutter

I want to implement story items as different widgets. Like in this example:
In this picture, only images are changed, but I want to change as whole widgets as story items.
I have tried the story_view package. But, in this package, only images and videos can be added. Is there any other library for that?
As explained by https://stackoverflow.com/users/8164116/daksh-gargas, story view can be easily implemented using stack pageview and a simple gesture detector.
Made a simple story view -
import 'package:flutter/material.dart';
class CustomStoryView extends StatefulWidget{
#override
_CustomStoryViewState createState() => _CustomStoryViewState();
}
class _CustomStoryViewState extends State<CustomStoryView> with SingleTickerProviderStateMixin {
final List _colorsList = [Colors.blue, Colors.red, Colors.green, Colors.yellow, Colors.grey, Colors.brown];
final PageController _controller = PageController();
double _progressIndicators;
int _page = 0;
AnimationController _animationController;
bool dragEnded = true;
Size _pageSize;
#override
void initState() {
_animationController = AnimationController(vsync: this, duration: Duration(seconds: 2));
_animationController.addListener(animationListener);
_animationController.forward();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_pageSize = MediaQuery.of(context).size;
_progressIndicators = (_pageSize.width - 100) / 6;
});
super.initState();
}
#override
void dispose() {
_animationController?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
PageView.builder(
controller: _controller,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index)=>GestureDetector(
onLongPressStart: _onLongPressStart,
onLongPressEnd: _onLongPressEnd,
onHorizontalDragEnd: _onHorizontalDragEnd,
onHorizontalDragStart: _onHorizontalDragStart,
onHorizontalDragUpdate: _onHorizontalDragUpdate,
onTapUp: _onTapDown,
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: _colorsList[index],
child: Center(child: InkWell(
onTap: (){
print("thiswasclicked $index");
},
child: Text("Somee random text", style: TextStyle(fontSize: 36),)),),
),
),
itemCount: _colorsList.length,
),
Positioned(
top: 48,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: ([0,1,2,3,4,5].map((e) =>
(e == _page) ? Stack(
children: [
Container(
width: _progressIndicators,
height: 8 ,
color: Colors.black54,
),
AnimatedBuilder(
animation: _animationController,
builder: (ctx, widget){
return AnimatedContainer(
width: _progressIndicators * _animationController.value,
height: 8 ,
color: Colors.white,
duration: Duration(milliseconds: 100),
);
},
),
],
): Container(
width: _progressIndicators,
height: 8 ,
color: (_page >= e) ? Colors.white : Colors.black54,
)).toList()),
),)
],
),
);
}
animationListener(){
if(_animationController.value == 1){
_moveForward();
}
}
_moveBackward(){
if(_controller.page != 0){
setState(() {
_page = (_controller.page - 1).toInt();
_page = (_page < 0) ? 0 : _page;
_controller.animateToPage(_page, duration: Duration(milliseconds: 100), curve: Curves.easeIn);
_animationController.reset();
_animationController.forward();
});
}
}
_moveForward(){
if(_controller.page != (_colorsList.length - 1)){
setState(() {
_page = (_controller.page + 1).toInt();
_controller.animateToPage(_page, duration: Duration(milliseconds: 100), curve: Curves.easeIn);
_animationController.reset();
_animationController.forward();
});
}
}
_onTapDown(TapUpDetails details) {
var x = details.globalPosition.dx;
(x < _pageSize.width / 2) ? _moveBackward() : _moveForward();
}
_onHorizontalDragUpdate(d){
if (!dragEnded) {
dragEnded = true;
if (d.delta.dx < -5) {
_moveForward();
} else if (d.delta.dx > 5) {
_moveBackward();
}
}
}
_onHorizontalDragStart(d) {
dragEnded = false;
}
_onHorizontalDragEnd(d) {
dragEnded = true;
}
_onLongPressEnd(_){
_animationController.forward();
}
_onLongPressStart(_){
_animationController.stop();
}
}
This can be easily achieved with Stack, Container, and a GestureDetector to switch between pages/stories.
Why Stacks?
Flutter's Stack is useful if you want to overlap several
children in a simple way, for example, having some text and an image,
overlaid with a gradient and a button attached to the bottom.
To handle your "fixed" views, which are, in this case:
Top Progress bar... you can create your custom progress bar if you want.
That image and the user name...
Let's call them myTopFixedWidgets()
Row(children: [CircleAvatar(...),Column(children: [Text(...),Text(...)],)],)
Now, put your Widget that you want to display and that changes (your "story") as the first item of the Stacks and place the Widgets 1. and 2. (mentioned above) in the second item of the list.
Maintain a variable index to choose the widget that you want to display.
Stack(
children: <Widget>[
widgetsToShowAsAStory[index],
myTopFixedWidgets() //mentioned above
],
)
Wrap it inside GestureDetector
List<Widget> widgetsToShowAsAStory = [];
var index = 0;
....
GestureDetector(
onTap: () {
//If the tap is on the LEFT side of the screen then decrement the value of the index
index-= 1; //(check for negatives)
//If the tap is on the RIGHT side of the screen then increment the value of the index
index+= 1; //(check for the size of list)
//call
setState() {}
},
child: Stack(
children: <Widget>[
widgetsToShowAsAStory[index],
myTopFixedWidgets()
],
),)
and boom, you're good to go!
I found solutions from the story_view. But it doesnot match my requirement. We can only show different widgets as stories items in story_view.We can't perform any actions on widgets. To implement this story_view and to show different widgets as stories. Do like this.
First import story_view flutter dependencies from here.
Then import this in main.dart file.
import "package:story_view/story_view.dart";
StoryView(
controller: controller,
storyItems: [
StoryItem.inlineImage(
url:
"https://images.unsplash.com/photo-1536063211352-0b94219f6212?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MXx8YmVhdXRpZnVsJTIwZ2lybHxlbnwwfHwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60",
controller: controller,
),
StoryItem(
new Container(
margin: EdgeInsets.all(12),
child: StaggeredGridView.countBuilder(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 12,
itemCount: imageList.length,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(
Radius.circular(15))),
child: ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(15)),
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: imageList[index],
fit: BoxFit.cover,
),
),
);
},
staggeredTileBuilder: (index) {
return StaggeredTile.count(
1, index.isEven ? 1.2 : 1.8);
}),
),
duration: aLongWeekend,
shown: true),
StoryItem(
new Container(
margin: EdgeInsets.all(12),
child: StaggeredGridView.countBuilder(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 12,
itemCount: imageList.length,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(
Radius.circular(15))),
child: ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(15)),
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: imageList[index],
fit: BoxFit.cover,
),
),
);
},
staggeredTileBuilder: (index) {
return StaggeredTile.count(
1, index.isEven ? 1.2 : 1.8);
}),
),
duration: aLongWeekend,
shown: true),
],
onStoryShow: (s) {
print("Showing a story");
},
onComplete: () {
print("Completed a cycle");
},
progressPosition: ProgressPosition.top,
repeat: false,
inline: false,
),