-
-
Notifications
You must be signed in to change notification settings - Fork 254
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
How use : AutomaticKeepAliveClientMixin #11
Comments
Hi there, What's the problem you're trying to solve? |
I am trying to convert a classic StatefulWidget that uses AutomaticKeepAliveClientMixin (which allows to preserve the scroll position) , with the ViewModel structure. For exemple : If we have Page1, Page2, Page3, Page4 based on the model (fetching data, and display a list) : class Page1 extends StatefulWidget {
@override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1>
with AutomaticKeepAliveClientMixin {. // <!-- How to use it with Stacked ???
List<dynamic> data = [];
Future<List<dynamic>> _data;
ScrollController _controller;
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
_data = fetchData();
_controller =
ScrollController(initialScrollOffset: 0.0, keepScrollOffset: true);
_controller.addListener(_scrollListener);
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
Future<List<dynamic>> fetchData(int page) async {
try {
var response = await http.get('$URL');
if (response.statusCode == 200) {
setState(() {
data(json.decode(response.body).map((m) => MyModel.fromJson(m)).toList());
});
return data;
} else { throw 'Data error'; }
} on SocketException { throw 'No Internet connection'; }
return data;
}
@override
Widget build(BuildContext context) {
super.build(context);
return FutureBuilder<List<dynamic>>(
future: data,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.length == 0) return Container();
return Column(
children: <Widget>[
Column(
children: snapshot.data.map((item) {
final heroId = item.id.toString() + "-latest";
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NewPage(item, heroId),
),
);
},
child: displayMyDataItem(context, item, heroId),
);
}).toList())
],
);
} else if (articleSnapshot.hasError) {
return Container();
}
}
} and a MainPage with a bottom navigation bar : class MainPage extends StatefulWidget {
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _selectedIndex = 0;
List<Widget> _pages = <Widget>[
Page1(),
Page2(),
Page3(),
Page4(),
];
PageController pageController = PageController();
void _onItemTapped(int index) {
pageController.jumpToPage(index);
}
void _onPageChanged(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body:PageView(
controller: pageController,
onPageChanged: _onPageChanged,
children: _pages,
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem( title: Text('Page 1')),
BottomNavigationBarItem( title: Text('Page 2')),
BottomNavigationBarItem( title: Text('Page 3')),
BottomNavigationBarItem( title: Text('Page 4')),
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
type: BottomNavigationBarType.shifting),
);
}
} If Page1, Page2, Page3, Page4 with the Stacked architecture : class Page1Screen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ViewModelBuilder<Page1ViewModel>.reactive(. // <-- Stacked !!! But no AutomaticKeepAliveClientMixin because it became a StatelessWidget
viewModelBuilder: () => Page1ViewModel(),
onModelReady: (model) => model.initialise(),
builder: (context, model, child) => Scaffold(
body: Container(
decoration: BoxDecoration(color: Colors.white70),
child: SingleChildScrollView(
controller: model.controller,
scrollDirection: Axis.vertical,
child: Column(
children: <Widget>[
displayData(model, model.data)
],
),
),
),
),
);
}
Widget displayData(Future<List<dynamic>> data) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: FutureBuilder<List<dynamic>>(
future: data,
builder: (context, articleSnapshot) {
if (articleSnapshot.hasData) {
if (articleSnapshot.data.length == 0) return Container();
return Row(
children: articleSnapshot.data.map((item) {
final heroId = item.id.toString() + "-featured";
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NewPage(item, heroId),
),
);
},
child: displayMyDataItem(context, item, heroId));
}).toList());
} else if (articleSnapshot.hasError) {
return Container(
alignment: Alignment.center,
margin: EdgeInsets.fromLTRB(0, 60, 0, 0),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
children: <Widget>[
Image.asset(
"assets/no-internet.png",
width: 250,
),
Text("No Internet Connection."),
FlatButton.icon(
icon: Icon(Icons.refresh),
label: Text("Reload"),
onPressed: () {
},
)
],
),
);
}
return Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
height: 280,
child: Loading( indicator: BallBeatIndicator(),size: 60.0, color: Theme.of(context).accentColor));
},
),
);
}
} and same for MainPage... It works, list are displayed .... but each time I click on a BottomNavigationBarItem, the list is refreshed, because I can't success to put the within AutomaticKeepAliveClientMixin which allows to keep the widget's state.. |
You can just make Page1Screen stateful. Stacked doesn't exempt you from using Stateful widgets. Because almost all state is manged in the viewmodel you just don't see it often but you can still use stateful widgets if you want to. |
I will try it ;-) |
Great ! It works ! class Page1Screen extends StatefulWidget {
Page1Screen({Key key}) : super(key: key);
@override
_Page1ScreenState createState() => _Page1ScreenState();
}
class _Page1ScreenState extends State<Page1Screen>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context);
return ViewModelBuilder<ArticlesViewModel>.reactive(
viewModelBuilder: () => ArticlesViewModel(),
onModelReady: (model) => model.initialise(),
builder: (context, model, child) => Scaffold(
...
)
);
}
} Thanks for your help! |
Awesome! Happy it works. I don't think that would be required since there's nothing saying you can ONLY use a stateless widget. Like everything else the ViewModelBuilder is a widget, so it can be used anywhere in flutter like every other widget. There's no limitation. |
Hello,
I am new in Flutter and I was looking for an architecture model.
After having seen your tutorial, I have decided to try to implement the Stacked philosophy.
I use some training codes I made to implement Stacked. Everything is fine with the View and Viewmodel.
In one code I use, there is a Scaffold and a bottomNavigationBar with 4 pages.
Each page has a list, and to preserve the scroll position, they implement : AutomaticKeepAliveClientMixin
The tutorial is : https://cantaspinar.com/persistent-bottom-navigation-bar-in-flutter/
How implement AutomaticKeepAliveClientMixin using Stacked? I have tried with
... extends ViewModelBuilderWidget with AutomaticKeepAliveClientMixin
but I have an error
Thanks for your help
The text was updated successfully, but these errors were encountered: