Created
March 19, 2019 12:40
-
-
Save brianegan/799c8390f50576137d5bd2e02129da9c to your computer and use it in GitHub Desktop.
Live Coded Scoped Model
This file contains hidden or 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 'package:flutter/material.dart'; | |
void main() => runApp(MyApp()); | |
class Counter extends ChangeNotifier { | |
int _count = 0; | |
int get count => _count; | |
void increment() { | |
_count++; | |
notifyListeners(); | |
} | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: MyHomePage(title: 'Flutter Demo Home Page'), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
MyHomePage({Key key, this.title}) : super(key: key); | |
final String title; | |
@override | |
_MyHomePageState createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
final _counter = Counter(); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(widget.title), | |
), | |
body: ScopedCounter( | |
child: HomePageBody(counter: _counter), | |
counter: _counter, | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: _counter.increment, | |
tooltip: 'Increment', | |
child: Icon(Icons.add), | |
), // This trailing comma makes auto-formatting nicer for build methods. | |
); | |
} | |
} | |
class HomePageBody extends StatelessWidget { | |
const HomePageBody({ | |
Key key, | |
@required Counter counter, | |
}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
Text( | |
'You have pushed the button this many times:', | |
), | |
ScopedCounterDescendant( | |
builder: (context, counter) { | |
return Text( | |
'${counter.count}', | |
style: Theme.of(context).textTheme.display1, | |
); | |
}, | |
), | |
], | |
), | |
); | |
} | |
} | |
class ScopedCounter extends StatelessWidget { | |
final Counter counter; | |
final Widget child; | |
const ScopedCounter({Key key, this.counter, this.child}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return AnimatedBuilder( | |
animation: counter, | |
builder: (context, _) { | |
return InheritedCounter( | |
counter: counter, | |
child: child, | |
); | |
}, | |
); | |
} | |
static Counter of(BuildContext context) { | |
return (context.inheritFromWidgetOfExactType(InheritedCounter) | |
as InheritedCounter) | |
.counter; | |
} | |
} | |
class InheritedCounter extends InheritedWidget { | |
final Counter counter; | |
const InheritedCounter({ | |
Key key, | |
@required Widget child, | |
@required this.counter, | |
}) : assert(child != null), | |
super(key: key, child: child); | |
@override | |
bool updateShouldNotify(InheritedCounter old) { | |
return true; | |
} | |
} | |
class ScopedCounterDescendant extends StatelessWidget { | |
final Widget Function(BuildContext, Counter) builder; | |
const ScopedCounterDescendant({Key key, this.builder}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
final counter = ScopedCounter.of(context); | |
return builder(context, counter); | |
} | |
} |
This file contains hidden or 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 'package:flutter/foundation.dart'; | |
class Model implements Listenable { | |
final _listeners = Set<void Function()>(); | |
bool get hasListeners => _listeners.isNotEmpty; | |
void addListener(void Function() listener) => _listeners.add(listener); | |
void removeListener(void Function() listener) => _listeners.remove(listener); | |
void notifyListeners() => _listeners.forEach((listener) => listener()); | |
} |
This file contains hidden or 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 'package:flutter_test/flutter_test.dart'; | |
import 'package:live_scoped_model/model.dart'; | |
void main() { | |
group('Model', () { | |
test('should add a listner', () { | |
final model = Model(); | |
model.addListener(() {}); | |
expect(model.hasListeners, isTrue); | |
}); | |
test('should remove a listener', () { | |
final model = Model(); | |
void listener() {} | |
model.addListener(listener); | |
model.removeListener(listener); | |
expect(model.hasListeners, isFalse); | |
}); | |
test('should notify', () { | |
final model = Model(); | |
int callCount = 0; | |
model.addListener(() { | |
callCount++; | |
}); | |
model.notifyListeners(); | |
expect(callCount, 1); | |
}); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Awesome concept