How can i add delay between items in listview.builder? - android

I have a ListView containing string items. I want to make it so that ListView's items show one by one with delay. And after each item appears, I want to wait about 2 seconds with a typing indicator and then the next item will appear. How will I do? Please give me the solution. Thanks!

You can use AnimatedListView like this code below
https://www.youtube.com/watch?v=ZtfItHwFlZ8
// main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Test',
theme: ThemeData(
primaryColor: Colors.green,
colorScheme:
ColorScheme.fromSwatch().copyWith(secondary: Colors.greenAccent)),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Items in the list
final _items = ["Item 0"];
// The key of the list
final GlobalKey<AnimatedListState> _key = GlobalKey();
// Add a new item to the list
// This is trigger when the floating button is pressed
void _addItem() {
_items.insert(0, "Item ${_items.length + 1}");
_key.currentState!.insertItem(0, duration: const Duration(seconds: 1));
}
// Remove an item
// This is trigger when an item is tapped
void _removeItem(int index, BuildContext context) {
AnimatedList.of(context).removeItem(index, (_, animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
child: SizedBox(
height: 150,
child: Card(
margin: const EdgeInsets.symmetric(vertical: 20),
elevation: 10,
color: Colors.red[400],
child: const Center(
child: Text("I am going away",
style: const TextStyle(fontSize: 28)),
),
),
),
),
);
}, duration: const Duration(seconds: 1));
_items.removeAt(index);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Test list'),
actions: [
IconButton(
onPressed: _addItem,
icon: const Icon(Icons.plus_one_outlined),
)
],
),
body: AnimatedList(
key: _key,
initialItemCount: 1,
padding: const EdgeInsets.all(10),
itemBuilder: (context, index, animation) {
return SlideTransition(
key: UniqueKey(),
position: Tween<Offset>(
begin: const Offset(-1, -0.5),
end: const Offset(0, 0),
).animate(animation),
child: RotationTransition(
turns: animation,
child: SizeTransition(
axis: Axis.vertical,
sizeFactor: animation,
child: SizedBox(
height: 150,
child: InkWell(
onTap: () => _removeItem(index, context),
child: Card(
margin: const EdgeInsets.symmetric(vertical: 20),
elevation: 10,
color: Colors.primaries[
(index * 100) % Colors.primaries.length][300],
child: Center(
child: Text(_items[index],
style: const TextStyle(fontSize: 28)),
),
),
),
),
),
),
);
},
),
);
}
}

Try wrapping your widget around this widget https://pub.dev/packages/delayed_display. Please confirm if this works.

Related

How to make page transition when use bottomNavigationBar in flutter

I use a basic bottomNavigationBar and now it shows just page without any transition,i want to use page transition when tap different bottomNavigationBars item.
Use IndexedStack Widget
int _currentIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: IndexedStack(
index: _currentIndex,
children: const [
HomePage(),
SettingsPage(),
],
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (int index) => setState(() => _currentIndex = index),
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
],
),
);
}
use PageView or TabView
Check this article
List pages = [
HomeScreen(),
DiscoverScreen(),
Container(),
MessageScreen(),
ProfileScreen(),
];
Scaffold(
body: pages.elementAt(current!),
bottomNavigationBar: BottomNavigationBar(
currentIndex: widget.current!,
unselectedItemColor: Colors.black,
selectedItemColor: Colors.green,
showSelectedLabels: true,
showUnselectedLabels: true,
onTap: (val) {
setState(() {
current = val;
});
Bottom Nav Transition With Persisting Ui
import 'package:flutter/material.dart';
class BottomNavTransitionWithPersistingUi extends StatefulWidget {
const BottomNavTransitionWithPersistingUi({Key? key}) : super(key: key);
#override
State<BottomNavTransitionWithPersistingUi> createState() => _BottomNavTransitionWithPersistingUiState();
}
class _BottomNavTransitionWithPersistingUiState extends State<BottomNavTransitionWithPersistingUi> {
late PageController _pageController;
late List<Widget> _tabBody;
int _currentIndex = 0;
#override
void initState() {
super.initState();
_pageController = PageController(
initialPage: 0,
keepPage: true, // for ui state
);
_tabBody = [
const NavHomeView(key: PageStorageKey("NavHomeView")),
const NavMoreView(key: PageStorageKey("NavMoreView")),
];
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: _bottomNavigationBarItems,
body: PageView(
controller: _pageController,
onPageChanged: _onPageChanged,
children: _tabBody,
),
);
}
Widget get _bottomNavigationBarItems => BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.menu),
label: 'More',
),
],
currentIndex: _currentIndex,
selectedItemColor: Colors.red,
elevation: 0.1,
unselectedItemColor: Colors.grey,
enableFeedback: true,
onTap: _onNavBarTapped,
);
void _onNavBarTapped(int index) {
setState(() {
_currentIndex = index;
});
_pageController.animateToPage(
index,
duration: const Duration(milliseconds: 500),
curve: Curves.easeOut,
);
}
void _onPageChanged(int index) {
setState(() {
_currentIndex = index;
});
}
}
class NavHomeView extends StatelessWidget {
const NavHomeView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView(
children: [
Container(
height: 500,
color: Colors.purple,
),
Container(
height: 500,
color: Colors.blue,
),
Container(
height: 500,
color: Colors.grey,
),
],
);
}
}
class NavMoreView extends StatelessWidget {
const NavMoreView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView(
children: [
Container(
height: 500,
color: Colors.red,
),
Container(
height: 500,
color: Colors.green,
),
Container(
height: 500,
color: Colors.amber,
),
],
);
}
}

How can I create inner navigation in showModalBottomSheet?

In my app I am trying to implement Badoo-like sort/filter showBottomModalSheet feature. I managed to create 2 separate pages, which I can navigate back and forth. However, the problem I'm facing is the second page in showBottomModalSheet. Back button works fine until I try to touch outside of the modal, which takes back to the first page. Instead it should close modal.
User navigates to sort users modal, which shows the 1st page in showBottomModalSheet When user taps "Show gender" it navigates to the second page (the one with different genders). When back button is pressed it navigates to 1st screen until it closes modal completely. Touching outside of the modal also closes the modal
The best stackoverflow answer that I tried:
https://stackoverflow.com/questions/63602999/how-can-i-do-navigator-push-in-a-modal-bottom-sheet-only-not-the-parent-page/63603685#63603685
I also tried using modal_bottom_sheet package, but had no luck.
https://pub.dev/packages/modal_bottom_sheet/example
Most of my code behind showBottomModalSheet:
class Page1 extends StatefulWidget {
const Page1({
Key? key
}) : super(key: key);
#override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
int _currentView = 0;
late List<Widget> pages;
#override
void initState() {
pages = [
page1(),
page2(),
];
super.initState();
}
#override
Widget build(BuildContext context) {
print("LOG build _currentView ${_currentView}");
return pages[_currentView];
}
Widget page1() {
return WillPopScope(
onWillPop: () async {
return true;
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topRight: Radius.circular(60), topLeft: Radius.circular(60))),
height: 400,
width: double.maxFinite,
child: Center(
child: Column(
children: [
Text("First page"),
ElevatedButton(
onPressed: () {
setState(() {
_currentView = 1;
print("LOG page1 _currentView ${_currentView}");
});
},
child: Text("tap to navigate to 2nd page"),
),
],
)),
));
}
Widget page2() {
return WillPopScope(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topRight: Radius.circular(60), topLeft: Radius.circular(60))),
height: 400,
width: double.maxFinite,
child: Center(
child: InkWell(
onTap: () {
setState(() {
_currentView = 0;
print("LOG page2 _currentView ${_currentView}");
});
},
child: Text("tap to navigate to 1st screen"),
),
),
),
onWillPop: () async {
print("LOG currentView jot $_currentView");
if (_currentView == 0) {
return true;
}
setState(() {
_currentView = 0;
});
return false;
});
}
}
Solution 1
Use the standard DraggableScrollableSheet or a 3rd-party widget to do it, there are a bunch of them. Here are some from https://pub.dev/
awesome_select
backdrop_modal_route
bottom_sheet_expandable_bar
bottom_sheet
cupertino_modal_sheet
modal_bottom_sheet
just_bottom_sheet
sheet
Solution 2
Anyway, if you'd like to do it manually I'd do it with Navigator.push/Navigator.pop instead with PageRouteBuilder with barrierDismissible=true.
It would like the following. Check out the live demo on DartPad.
Here's the code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _show() async {
await Navigator.of(context).push(
PageRouteBuilder(
opaque: false,
barrierDismissible: true,
pageBuilder: (_, __, ___) => const Page1(),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: _show,
tooltip: 'Settings',
child: const Icon(Icons.settings),
),
);
}
}
class Page1 extends StatefulWidget {
const Page1({Key? key}) : super(key: key);
#override
State<Page1> createState() => _Page1State();
}
class _Page1State extends State<Page1> {
#override
Widget build(BuildContext context) {
return Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.only(
topRight: Radius.circular(60), topLeft: Radius.circular(60)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3), // changes position of shadow
),
],
),
height: 400,
width: double.maxFinite,
child: Center(
child: Material(
type: MaterialType.transparency,
child: Column(
children: [
const Text("First page"),
ElevatedButton(
onPressed: () async {
final backButton =
await Navigator.of(context).push<bool?>(
PageRouteBuilder(
opaque: false,
barrierDismissible: true,
pageBuilder: (_, __, ___) => const Page2(),
),
);
if (backButton == null || backButton == false) {
if (mounted) Navigator.of(context).pop();
}
},
child: const Text("tap to navigate to 2nd page"),
),
],
),
),
),
),
),
],
);
}
}
class Page2 extends StatelessWidget {
const Page2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topRight: Radius.circular(60), topLeft: Radius.circular(60)),
),
height: 400,
width: double.maxFinite,
child: Center(
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () => Navigator.of(context).pop(true),
child: const Text("tap to navigate to 1st screen"),
),
),
),
),
);
}
}

Encaplsulating a widget for use in another dart file

What I'm trying to achieve:
Have a BottomNavigationBar widget in its own class in its own dart file called navigationBar.dart
Have a main.dart file that has a Scaffold widget that calls this class to create the BottomNavigationBar widget
Then in the main.dart file I want to be able to set the BottomNavigationBar from navigationBar.dart and I want to be able to change the body of the Scaffold widget in the main.dart file depending on which index is selected in the BottomNavigationBar widget (check the comment in the main.dart file in the body property for a better explanation)
Here is my code below so far:
navigationBar.dart
import 'package:flutter/material.dart';
import '../home.dart';
class NavigationBar extends StatefulWidget {
const NavigationBar({Key? key}) : super(key: key);
#override
State<NavigationBar> createState() => _NavigationBar();
}
class _NavigationBar extends State<NavigationBar> {
int selectedIndex = 2;
void _onItemTapped(int index) {
setState(() {
selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.red,
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
backgroundColor: Colors.green,
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
backgroundColor: Colors.purple,
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
backgroundColor: Colors.pink,
),
],
currentIndex: selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
);
}
main.dart
import 'package:flutter/material.dart';
import 'components/navigationBar.dart';
void main() {
runApp(const MaterialApp(home: App()));
}
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
#override
State<App> createState() => _App();
}
class _App extends State<App> {
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
),
Text(
'Index 1: Business',
),
Text(
'Index 2: School',
),
Text(
'Index 3: Settings',
),
];
#override
Widget build(BuildContext context) {
const navBar = navigationBar()
return Scaffold(
appBar: AppBar(
title: const Text('Test App',
style: TextStyle(
color: Colors.white,
fontFamily: 'LogoFont',
fontSize: 30.0,
letterSpacing: 1.5)),
centerTitle: true,
backgroundColor: Colors.lightBlue[500],
elevation: 0.0,
),
backgroundColor: Colors.lightBlue[800],
body: //something like this: _widgetOptions.elementAt(navbar.selectedIndex)
),
bottomNavigationBar: navBar);
}
}
Any ideas on how I could create what I need in the bullet points? Any help would be great, thanks
I think there is no way with stateful widget but you can do this by provider, like example below.
Provider:
class MainViewProvider with ChangeNotifier ,
DiagnosticableTreeMixin{
int activeItem = 2;
changeActiveItem(int activeElement){
activeItem = activeElement;
notifyListeners();
}
}
BottomNavBar Widget:
class BotNavWidget extends StatelessWidget {
const BotNavWidget({Key? key}) : super(key: key);
get context => null;
#override
Widget build(BuildContext context) {
final watch = context.watch<ColorsProvider>();
return Container(
padding: EdgeInsets.symmetric(
horizontal: getWidth(16), vertical: getHeight(10)),
child: Container(
height: SizeConfig.height! * .1,
width: SizeConfig.width!,
decoration: BoxDecoration(
color: watch.colors[1],
borderRadius: BorderRadius.circular(getWidth(20))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
buildIcon(watch.bottomIcons[0], context, 0),
buildIcon(watch.bottomIcons[1], context, 1),
buildIcon(watch.bottomIcons[2], context, 2),
buildIcon(watch.bottomIcons[3], context, 3),
buildIcon(watch.bottomIcons[4], context, 4),
],
),
),
);
}
buildIcon(ColorFiltered icon, BuildContext context, int i) {
final read = context.read<MainViewProvider>();
final watch = context.watch<MainViewProvider>();
final watchColors = context.watch<ColorsProvider>().colors;
return InkWell(
onTap: () async{
read.changeActiveItem(i);
},
child: Container(
padding: EdgeInsets.all(getWidth(15)),
height: SizeConfig.height! * .07,
width: SizeConfig.height! * .07,
decoration: BoxDecoration(
color: watch.activeItem == i ? watchColors[2] : Colors.transparent,
borderRadius: BorderRadius.circular(getWidth(20))),
child: SizedBox(
height: getHeight(24),
width: getHeight(24),
child: icon
),
),
);
}
}
P.S: You can use custom BottomNavigationBar Widget instead of making it manually

How to open this type of alert dialog in flutter

I wanted to show dialog in my application. How can i achieve this using flutter
You can use a PopupMenuButton (https://api.flutter.dev/flutter/material/PopupMenuButton-class.html) to achieve this in flutter.
See example code below:
PopupMenuButton<int>(
itemBuilder: (context) => [
const PopupMenuItem(
value: 1,
child: Center(
child: Icon(
Icons.download_outlined,
size: 30.0,
),
),
),
const PopupMenuItem(
value: 2,
child: Center(
child: Icon(
Icons.link,
size: 30.0,
),
),
),
const PopupMenuItem(
value: 2,
child: Center(
child: Icon(
Icons.share,
size: 30.0,
),
),
),
],
icon: const Icon(
Icons.more_horiz,
size: 40.0,
),
offset: const Offset(150, -150),
);
The above example popups a list of Icons when the PopupMenuButton is pressed.
You can adapt this to your use-case above.
Finally I found a Solution thanks enfinity. Here how i solve the problem.
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(),
);
}
}
/// An arbitrary widget that lives in a popup menu
class PopupMenuWidget<T> extends PopupMenuEntry<T> {
const PopupMenuWidget({ Key key, this.height, this.child }) : super(key: key);
#override
final Widget child;
#override
final double height;
#override
bool get enabled => false;
#override
_PopupMenuWidgetState createState() => new _PopupMenuWidgetState();
}
class _PopupMenuWidgetState extends State<PopupMenuWidget> {
#override
Widget build(BuildContext context) => widget.child;
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
actions: <Widget>[
new PopupMenuButton<String>(
onSelected: (String value) {
print("You selected $value");
},
itemBuilder: (BuildContext context) {
return [
new PopupMenuWidget(
height: 40.0,
child: new Row(
children: [
IconButton(
icon: Icon(
Icons.remove,
color: Colors.green,
),
onPressed: () {
print("Remove");
}),
Text("1"),
IconButton(
icon: Icon(
Icons.add,
color: Colors.green,
),
onPressed: () {
print("Add");
}),
],
),
),
];
}
),
],
),
);
}
}

How to dismiss item w/ Dismissible widget by tap in AnimatedBuilder?

I'n new in flutter. I have a playground app and I'm trying to dismiss the item from the list. I found this example without dismiss functionality. It works fine by swiping in a specific direction, but what I need is dismissing it by taping on a number on the card and remove swiping functionality at all. Thanks in advance.
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class ColorCards {
int id;
Color color;
ColorCards({this.id, this.color});
}
class _MyHomePageState extends State<MyHomePage> {
PageController controller;
int currentPage = 0;
List<ColorCards> cards = [
ColorCards(id: 1, color: Colors.green),
ColorCards(id: 2, color: Colors.yellow),
ColorCards(id: 3, color: Colors.green),
ColorCards(id: 4, color: Colors.yellow),
ColorCards(id: 5, color: Colors.green),
];
#override
initState() {
super.initState();
controller = PageController(
initialPage: currentPage,
keepPage: false,
viewportFraction: 0.85,
);
}
#override
dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
//Scaffold
return Scaffold(
appBar: AppBar(
title: Text('Playground App'),
backgroundColor: Colors.grey,
),
body: Center(
child: Container(
child: PageView.builder(
onPageChanged: (value) {
setState(() {
currentPage = value;
});
},
itemCount: cards.length,
controller: controller,
itemBuilder: (context, index) => builder(index)),
),
),
);
}
builder(int index) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
double value = 1.0;
if (controller.position.haveDimensions) {
value = controller.page - index;
value = (1 - (value.abs() * .1)).clamp(0.0, 1.0);
}
return Center(
child: Dismissible(
key: Key(cards[index].id.toString()),
direction: DismissDirection.up,
onDismissed: (direction) {
cards.removeAt(index);
setState(() {
controller.animateToPage(index,
duration: Duration(milliseconds: 1), curve: Curves.bounceIn);
});
},
child: SizedBox(
height: Curves.easeOut.transform(value) * 300,
width: Curves.easeOut.transform(value) * 350,
child: child,
),
),
);
},
child: Container(
child: Center(
child: GestureDetector(
onTap: () {
print('Dismiss this item');
},
child: Text(
cards[index].id.toString(),
style: TextStyle(
fontSize: 60.0, color: Colors.white, fontWeight: FontWeight.bold),
),
)),
margin: const EdgeInsets.all(8.0),
color: cards[index].color),
);
}
}
For those who got in same situation, I've found the workaround. First, its better to use AnimatedList instead of PageView. Detailed information about AnimatedList you can find here and here. Then you have to specify Dismissible in ItemBuilder class. And that's it, hope it

Categories

Resources