based on The Design picture you provided, Instead of a grid view, you actually need a Carousel, or a page, to implement the pagination.
[carousel_builder][1] package or PageView widget is the solution for implementing the pagination.
to achieve the dots indicator, you can use [dots_indicator][2] mentioned in the comments.
then you should use your pageview or carousel controller, and on changing the index, update your indicator.
I'll provide the code of an image_slider I implemented, you can edit and achieve what you want:
class ImageSlider extends StatefulWidget {
final List<String> urls;
final bool openImagePopUp;
final PageController sliderController;
const ImageSlider(
{Key key, this.urls, this.openImagePopUp = false, this.sliderController})
: super(key: key);
@override
_ImageSliderState createState() => _ImageSliderState();
}
class _ImageSliderState extends State<ImageSlider> {
int currentSlideIndex;
PageController sliderController;
void setCurrentImageIndex(int newIndex) {
setState(() {
currentSlideIndex = newIndex;
});
}
@override
void initState() {
currentSlideIndex = 0;
sliderController =
widget.sliderController ?? PageController(initialPage: 0);
super.initState();
}
@override
void dispose() {
sliderController.dispose();
super.dispose();
}
void nextPage() {
sliderController.nextPage(
duration: Duration(milliseconds: 250), curve: Curves.easeOut);
}
void previousPage() {
sliderController.previousPage(
duration: Duration(milliseconds: 250), curve: Curves.easeOut);
}
@override
Widget build(BuildContext context) {
return Stack(children: [
Directionality(
textDirection: TextDirection.ltr,
child: PageView.builder(
controller: sliderController,
onPageChanged: (newIndex) {
setCurrentImageIndex(newIndex);
},
itemCount: widget.urls.length,
itemBuilder: (context, index) {
return InkWell(
onTap: widget.openImagePopUp
? () {
context.read<NavigationService>().showPopUp(
ProductDetailsImagePopUp(urls: widget.urls), context);
}
: null,
child: Image.network(
createImageUrl(widget.urls[index]),
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
return LoadImageErrorWidget();
},
),
);
},
),
),
if (widget.urls.length > 1) ...[
Positioned(
left: 0,
bottom: 10,
child: CircleIndicators(
index: currentSlideIndex,
length: widget.urls.length,
),
),
Center(
child: Directionality(
textDirection: TextDirection.rtl,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (currentSlideIndex < widget.urls.length - 1)
IconButton(
icon: Icon(Icons.chevron_left), onPressed: nextPage),
Expanded(child: Container()),
if (currentSlideIndex > 0)
IconButton(
icon: Icon(Icons.chevron_right), onPressed: previousPage),
],
),
),
)
]
]);
}
}
[1]: https://pub.dev/packages/carousel_slider
[2]: https://pub.dev/packages/dots_indicator
**Step 1: Create Dialog**
showAlertDialog(BuildContext context){
AlertDialog alert=AlertDialog(
content: new Row(
children: [
CircularProgressIndicator(),
Container(margin: EdgeInsets.only(left: 5),child:Text("Loading" )),
],),
);
showDialog(barrierDismissible: false,
context:context,
builder:(BuildContext context){
return alert;
},
);
}
**Step 2:Call it**
showAlertDialog(context);
await firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
Navigator.pop(context);
**Example With Dialog and login form**
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class DynamicLayout extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new MyWidget();
}
}
showAlertDialog(BuildContext context){
AlertDialog alert=AlertDialog(
content: new Row(
children: [
CircularProgressIndicator(),
Container(margin: EdgeInsets.only(left: 5),child:Text("Loading" )),
],),
);
showDialog(barrierDismissible: false,
context:context,
builder:(BuildContext context){
return alert;
},
);
}
class MyWidget extends State<DynamicLayout>{
Color color = Colors.indigoAccent;
String title='app';
GlobalKey<FormState> globalKey=GlobalKey<FormState>();
String email,password;
login() async{
var currentState= globalKey.currentState;
if(currentState.validate()){
currentState.save();
FirebaseAuth firebaseAuth=FirebaseAuth.instance;
try {
showAlertDialog(context);
AuthResult authResult=await firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser user=authResult.user;
Navigator.pop(context);
}catch(e){
print(e);
}
}else{
}
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar:AppBar(
title: Text("$title"),
) ,
body: Container(child: Form(
key: globalKey,
child: Container(
padding: EdgeInsets.all(10),
child: Column(children: <Widget>[
TextFormField(decoration: InputDecoration(icon: Icon(Icons.email),labelText: 'Email'),
// ignore: missing_return
validator:(val){
if(val.isEmpty)
return 'Please Enter Your Email';
},
onSaved:(val){
email=val;
},
),
TextFormField(decoration: InputDecoration(icon: Icon(Icons.lock),labelText: 'Password'),
obscureText: true,
// ignore: missing_return
validator:(val){
if(val.isEmpty)
return 'Please Enter Your Password';
},
onSaved:(val){
password=val;
},
),
RaisedButton(color: Colors.lightBlue,textColor: Colors.white,child: Text('Login'),
onPressed:login),
],)
,),)
),
);
}
}
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/KXkVJ.jpg