According to many Flutter provider tutorials, it is recommended to use StatelessWidget instead of StatefulWidget with provider. But I can't find a way to scroll ListView when using StatelessWidget.
Suppose we have a ListView with many lines of text, which can be dynamically appended, and should always scroll to the middle line. Like this:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyData with ChangeNotifier {
List<String> _lines = [];
MyData() {
for (int i = 0; i < 100; i++) {
_lines.add("line $i");
}
}
List<String> get lines => _lines;
void add(line) {
_lines.add(line);
notifyListeners();
}
}
void main() {
MyData data = new MyData();
final app = ChangeNotifierProvider<MyData>.value(value: data, child: MyApp());
runApp(app);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: MyHomePage(),
);
}
}
class DataPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final _data = Provider.of<MyData>(context);
return Scaffold(
appBar: AppBar(),
body: ListView.builder(
itemCount: _data.lines.length,
itemBuilder: (context, index) {
return Text(_data.lines[index]);
}));
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final _data = Provider.of<MyData>(context);
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () {
_data.add("new line");
})
],
),
body: Center(child: Text("${_data.lines.length} lines")),
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => DataPage())),
child: Icon(Icons.list),
));
}
}
Is there any way to keep scroll to the middle of the ListView when new line added?
with WidgetsBinding.instance, scrollTo calls after the widget ist build. Everytime when the Provider MyData send a notify
i hope that's ok for you, height is a requirement
class DataPage extends StatelessWidget {
final _controller = ScrollController();
final _height = 30.0;
@override
Widget build(BuildContext context) {
final _data = Provider.of<MyData>(context);
scrollTo() {
print(_data.lines.length);
_controller.jumpTo(_data.lines.length / 4 * _height);
}
WidgetsBinding.instance
.addPostFrameCallback((_) => scrollTo());
return Scaffold(
appBar: AppBar(),
body: ListView.builder(
controller: _controller,
itemCount: _data.lines.length,
itemBuilder: (context, index) {
return new Container(child: Text(_data.lines[index]), height: _height,);
}));
}
}