Created
November 17, 2021 13:06
-
-
Save shriharip/c8fdb38d8ab42ad0d23d86f2ada4af12 to your computer and use it in GitHub Desktop.
listview flutter with out rebuilding the complete list (with null safety)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'package:flutter/material.dart'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatefulWidget { | |
@override | |
_MyAppState createState() => _MyAppState(); | |
} | |
class _MyAppState extends State<MyApp> { | |
final _navigatorKey = GlobalKey<NavigatorState>(); | |
FakeApi? _api; | |
@override | |
void initState() { | |
_api = FakeApi(_navigatorKey); | |
super.initState(); | |
} | |
@override | |
void dispose() { | |
_api?.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) => MaterialApp( | |
navigatorKey: _navigatorKey, | |
home: MyInheritedWidget(_api!, child: const MyHomePage()), | |
); | |
} | |
class MyInheritedWidget extends InheritedWidget { | |
final FakeApi api; | |
const MyInheritedWidget(this.api, {required Widget child}) | |
: super(child: child, key: const Key('MyInheritedWidget')); | |
static MyInheritedWidget? of(BuildContext context) => | |
context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>(); | |
@override | |
bool updateShouldNotify(MyInheritedWidget old) => false; | |
} | |
class MyHomePage extends StatelessWidget { | |
const MyHomePage() : super(key: const Key('MyHomePage')); | |
@override | |
Widget build(BuildContext context) => Builder( | |
builder: (context) => Scaffold( | |
backgroundColor: Colors.blueGrey, | |
body: StreamBuilder<List<ItemWidget>>( | |
stream: MyInheritedWidget.of(context)!.api.stream, | |
initialData: const [], | |
builder: (context, list) => list.hasError | |
? const Center(child: Icon(Icons.error)) | |
: !list.hasData | |
? const Center(child: CircularProgressIndicator()) | |
: list.data!.isEmpty | |
? const Center( | |
child: Text( | |
'the list is empty', | |
textScaleFactor: 1.5, | |
)) | |
: ListView.builder( | |
itemCount: list.data!.length, | |
itemBuilder: (context, index) { | |
print('the list is building'); | |
return list.data![index]; | |
}, | |
), | |
), | |
floatingActionButton: FloatingActionButton( | |
backgroundColor: Colors.white, | |
child: const Icon(Icons.add, color: Colors.blueGrey), | |
onPressed: MyInheritedWidget.of(context)!.api.add, | |
), | |
), | |
); | |
} | |
class ItemWidget extends StatelessWidget { | |
ItemWidget(this.text) : super(key: UniqueKey()); | |
final String text; | |
@override | |
Widget build(BuildContext context) { | |
print('Item $text is building'); | |
return Center( | |
child: Container( | |
padding: const EdgeInsets.only(bottom: 20), | |
width: MediaQuery.of(context).size.width * .5, | |
child: Card( | |
elevation: 10, | |
child: ListTile( | |
leading: GestureDetector( | |
child: const Icon(Icons.edit), | |
onTap: () => MyInheritedWidget.of(context)!.api.edit(key!), | |
), | |
trailing: GestureDetector( | |
child: const Icon(Icons.delete), | |
onTap: () => MyInheritedWidget.of(context)!.api.delete(key!), | |
), | |
title: Text(text), | |
), | |
), | |
), | |
); | |
} | |
} | |
class ItemDialog extends StatefulWidget { | |
const ItemDialog(this.text); | |
final String text; | |
@override | |
_ItemDialogState createState() => _ItemDialogState(); | |
} | |
class _ItemDialogState extends State<ItemDialog> { | |
TextEditingController? _controller; | |
@override | |
void initState() { | |
_controller = TextEditingController()..text = widget.text; | |
super.initState(); | |
} | |
@override | |
void dispose() { | |
_controller?.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) => AlertDialog( | |
content: Stack( | |
alignment: Alignment.center, | |
children: <Widget>[ | |
SizedBox( | |
width: double.infinity, | |
height: MediaQuery.of(context).size.height * .3, | |
child: Center( | |
child: TextField( | |
autofocus: true, | |
controller: _controller, | |
), | |
), | |
), | |
], | |
), | |
actions: <Widget>[ | |
IconButton( | |
onPressed: () => Navigator.pop(context, _controller!.text), | |
icon: const Icon(Icons.save), | |
), | |
], | |
); | |
} | |
class FakeApi { | |
FakeApi(this.navigatorKey); | |
final GlobalKey<NavigatorState> navigatorKey; | |
final _list = <ItemWidget>[]; | |
StreamController<List<ItemWidget>> _controller = StreamController(); | |
StreamController<List<ItemWidget>> get _c => | |
_controller = StreamController<List<ItemWidget>>.broadcast(); | |
Stream<List<ItemWidget>> get stream => _c.stream; | |
void dispose() => _controller.close(); | |
void delete(Key key) { | |
_list.removeWhere((ItemWidget item) => item.key == key); | |
_c.sink.add(_list); | |
} | |
void edit(Key key) async { | |
final _item = _list.firstWhere((ItemWidget item) => item.key == key); | |
final _index = _list.lastIndexOf(_item); | |
final _text = await showDialog<String>( | |
context: navigatorKey.currentState!.overlay!.context, | |
builder: (context) => ItemDialog( | |
_item.text, | |
), | |
); | |
_list.removeAt(_index); | |
_list.insert(_index, ItemWidget(_text!)); | |
_c.sink.add(_list); | |
} | |
void add() async { | |
final _text = await showDialog<String>( | |
context: navigatorKey.currentState!.overlay!.context, | |
builder: (context) => const ItemDialog(''), | |
); | |
_list.add(ItemWidget(_text!)); | |
_c.sink.add(_list); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment