Если я правильно понял, вы пытаетесь смоделировать PageView
навигацию по некоторому кружку ниже (Индикаторы).
Для этого есть много хороших ресурсов, а также такие пакеты, как: This пример или этот пакет
Но для вашего кода я написал его в 2 подходах:
Первый подход
Это ваш код и поставщик услуг.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(Home());
}
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => WelcomeBloc(),
child: Consumer<WelcomeBloc>(
builder: (BuildContext context, value, Widget child) {
PageController controller = value.controller;
print('object');
return MaterialApp(
home: Scaffold(
body: Stack(
children: <Widget>[
PageView(
controller: controller,
children: List.generate(
10, (i) => Center(child: Text('Page $i'))),
onPageChanged: (i) {
value.currentPage = i;
},
),
Footer(
activeColor: Colors.red,
duration: Duration(seconds: 1),
inactiveColor: Colors.yellow,
onFinal: () {},
onStart: () {},
totalSteps: 10,
)
],
),
),
);
},
),
);
}
}
class Footer extends StatefulWidget {
final int totalSteps;
final Color activeColor;
final Color inactiveColor;
final Duration duration;
final Function onFinal;
final Function onStart;
final double radius;
final double distance;
Footer({
this.activeColor,
this.inactiveColor,
this.totalSteps,
this.duration,
this.onFinal,
this.onStart,
this.radius = 10.0,
this.distance = 4.0,
});
@override
_FooterState createState() => _FooterState();
}
class _FooterState extends State<Footer> {
bool lastPage;
WelcomeBloc _welcomeBloc;
@override
Widget build(BuildContext context) {
final WelcomeBloc _welcome = Provider.of<WelcomeBloc>(context);
_welcomeBloc = _welcome;
// this._welcomeBloc.lastPage = true; // I'd like to set the state here
return Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_makeStepper(),
_makeNextArrow(),
],
),
);
}
_makeCircle(activeColor, inactiveColor, position, currentStep) {
currentStep = currentStep ?? 0;
Color color = (position == currentStep) ? activeColor : inactiveColor;
return Container(
height: widget.radius,
width: widget.radius,
margin: EdgeInsets.only(left: widget.distance, right: widget.distance),
decoration: BoxDecoration(
color: color,
border: Border.all(color: activeColor, width: 2.0),
borderRadius: BorderRadius.circular(50.0)),
);
}
_makeNextArrow() {
return Container(
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: GestureDetector(
onTap: () async {
await _welcomeBloc.nextPage(widget.duration, Curves.easeInOut);
setState(() {});
},
child: Icon(
Icons.arrow_forward,
)),
),
);
}
_makeStepper() {
return Container(
child: Row(
children: List.generate(
widget.totalSteps,
(i) => _makeCircle(
this.widget.activeColor,
this.widget.inactiveColor,
i,
_welcomeBloc.currentPage,
),
),
),
);
}
_onLastPage() {
if (this.widget.onFinal != null) {
this.widget.onFinal();
}
}
_onFirstPage() {
if (this.widget.onStart != null) {
this.widget.onStart();
}
}
_detectLastPage() {
int currentPage =
_welcomeBloc.currentPage == null ? 1 : _welcomeBloc.currentPage + 1;
if (currentPage == 1 && _welcomeBloc.currentPage == null) {
this._onFirstPage();
} else if (currentPage == this.widget.totalSteps) {
this.lastPage = true;
this._onLastPage();
} else {
this.lastPage = false;
}
}
}
class WelcomeBloc extends ChangeNotifier {
final PageController _controller = PageController();
int _currentPage = 0;
bool _lastPage = false;
bool get lastPage => _lastPage;
set lastPage(bool value) {
_lastPage = value;
notifyListeners();
}
int get currentPage => _currentPage;
set currentPage(int value) {
_currentPage = value;
notifyListeners();
}
PageController get controller => _controller;
Future<void> nextPage(Duration duration, Curve curve) {
currentPage = controller.page.floor() + 1;
return controller.nextPage(duration: duration, curve: curve);
}
}
Второй подход
Во втором я удалил материал провайдера, потому что это можно сделать без него с помощью PageController
функции.
import 'package:flutter/material.dart';
void main() {
runApp(Home());
}
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
PageController controller = PageController(initialPage: 0);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(
children: <Widget>[
PageView(
controller: controller,
children: List.generate(
10,
(i) => Center(child: Text('Page $i')),
),
onPageChanged: (page) {
setState(() {});
},
),
Footer(
currentPage: controller.hasClients ? controller.page.floor() : 0,
activeColor: Colors.red,
inactiveColor: Colors.yellow,
totalSteps: 10,
onTap: () async {
await controller.nextPage(
duration: Duration(seconds: 1) ?? Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
setState(() {});
},
)
],
),
),
);
}
}
class Footer extends StatelessWidget {
final int totalSteps;
final Color activeColor;
final Color inactiveColor;
final double radius;
final double distance;
final int currentPage;
final GestureTapCallback onTap;
Footer({
this.activeColor,
this.inactiveColor,
this.totalSteps,
this.radius = 10.0,
this.distance = 4.0,
this.currentPage,
this.onTap,
});
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_makeStepper(),
_makeNextArrow(),
],
),
);
}
_makeCircle(activeColor, inactiveColor, position) {
Color color = (position == currentPage) ? activeColor : inactiveColor;
return Container(
height: radius,
width: radius,
margin: EdgeInsets.only(left: distance, right: distance),
decoration: BoxDecoration(
color: color,
border: Border.all(color: activeColor, width: 2.0),
borderRadius: BorderRadius.circular(50.0)),
);
}
_makeNextArrow() {
return Container(
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: GestureDetector(
onTap: onTap,
child: Icon(
Icons.arrow_forward,
)),
),
);
}
_makeStepper() {
return Container(
child: Row(
children: List.generate(
totalSteps,
(i) => _makeCircle(
this.activeColor,
this.inactiveColor,
i,
),
),
),
);
}
}