Telescope
Easy to use State manager for flutter based on observer👀 design pattern.
Note: There is a 1.x.x branch for old friends here but version 2.x.x is much better and I suggest a refactor.
Telescope is more than a normal observer.
Telescope🔭
- Supports all platforms.
- Easy to learn 📖
- Easy to use 🫶:
- You can create an Automatically updatable Widget with a single
liveWidgetcall. - Works with StateLessWidgets.
- It Does call
setState()for you on value change detection.
- You can create an Automatically updatable Widget with a single
- Efficient 🏎️:
- It only rebuilds small parts of page🪶.
- Only rebuilds when needed.
- Smart Disposal🗑️.
- less then 900KB 🐣.
- Feature rich ♥️:
- It can save your states on disk if you want (good for user settings).
- Telescopes can depends on each other by using
dependsOn()constructor. - Depends on can be async.
- Caching ability with
expireTimeoption. debounceTimeoption (something like rx-js debounceTime).- Request a feature here.
- Flexible 🌊
- It lets you do it in your way as a library (not a framework) 🗽.
- Can be used beside other state managers 🤝.
Installation:
flutter pub add telescope
How to use
3 simple steps.
- Create a Telescope instance
- Make a live widget with
liveWidget()call. - Update state
- Boom widget automatically got updated without
setStatecall.
Example
import 'package:telescope/telescope.dart';
@override
class TextSample extends StatelessWidget {
final textValue = Telescope(""); // Telescope instance with default empty string
final style = const TextStyle(fontSize: 60);
TextSample({super.key});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => textValue.value += "a", // updating telescope value
child: Container(
color: Colors.white,
child: Column(
children: [
// multiple update subscribers connected to single Telescope
textValue.liveWidget((context, value) => Text(value, style: style)), // automatic updates
textValue.liveWidget((context, value) => Text(value, style: style)), // automatic updates
textValue.liveWidget((context, value) => Text(value.length.toString(), style: style)), // automatic updates
],
),
),
);
}
}
Result:
Note: You can also subscribe to observable by passing callback like a normal observable. (Just in case) 🤷🏻
textValue.subscribe((newValue){
// execute on value change
});
Parent/Child relation
You can pass telescopes around freely,
See (example).
Non Builtin Types
Just implement hashCode getter:
class Human{
int height;
int weight;
Human(this.height,this.weight);
@override
int get hashCode => Object.hash(name.hashCode, age.hashCode); // recommended way
}
And you are good to go:
var human = Telescope<Human?>(null);
Smart change detection needs hashCode() function to detect change (so that's why).
But if you don't want to do this for some reason there is a way out:
You can pass iWillCallNotifyAll = true and disable smart change detection:
var human = Telescope<Human>(Human("Ali", 24), iWillCallNotifyAll: true);
And make sure you call notifyAll() function manually after a change.
human.value.age = 30;
human.notifyAll();
Other features:
Depends on:
Telescopes can be depended on other telescopes.
var height = Telescope(186);
var weight = Telescope(72);
var bmi = Telescope.dependsOn([height,weight], () {
return weight.value / ((height.value/100) * (height.value/100));
});
var showingText = Telescope.dependsOn([bmi], () {
return "weight is ${weight.value} and height is ${height.value} so bmi will be ${bmi.value.toString().substring(0,5)}";
});
So when ever height or weight value get changes, the bmi will calculate itself because it depends on height and weight.
And showingText will calculate itself too, because it depends on bmi.
Async way:
var bmi = Telescope.dependsOnAsync(0, [height, weight], () async {
return await calculateBMI(height.value, weight.value);
});
Caching:
var bmi = Telescope.dependsOnAsync(0, [height, weight], () async {
return await calculateBMI(height.value, weight.value);
}, enableCaching: true);
You can also set expire time by passing cacheExpireTime.
Debounce:
debounceTime: will call your async function only if a given time has passed without any changes on dependencies.
var bmi = Telescope.dependsOnAsync(0, [height, weight], () async {
return await calculateBMI(height.value, weight.value);
}, debounceTime: Duration(milliseconds: 500));
It's useful when you want to run your async function when user stop typing or moving slider or...
Observable on calculating/loading state:
This will make isCalculatingBMI true on loading and false when loaded, you may need this to show loading animation.
var isCalculatingBMI = Telescope<bool>(false);
var bmi = Telescope.dependsOnAsync(0, [height, weight], () async {
return await calculateBMI(height.value, weight.value);
}, isCalculating: isCalculatingBMI);
Save On Disk
You can save telescope data on disk easily like this:
var height = Telescope.saveOnDiskForBuiltInType(187, "bmi_height_input");
So if user close the app and open it again it will load last value of telescope for You.
Save non built-in values on disk
You need to implement OnDiskSaveAbility for your object:
For example you have Human class:
class Human{
int height;
int weight;
Human(this.height,this.weight);
@override
int get hashCode => height*weight;
}
Then you need to make other class like this for Human:
class HumanOnDiskAbility implements OnDiskSaveAbility<Human>{
@override
Human parseOnDiskString(String data) {
var sp = data.split(":");
return Human(int.parse(sp[0]), int.parse(sp[1]));
}
@override
String toOnDiskString(Human instance) => "${instance.height}:${instance.weight}";
}
And pass instance of HumanOnDiskAbility to Telescope:
var human = Telescope.saveOnDiskForNonBuiltInType(
Human(187, 72),
"human_for_bmi",
HumanOnDiskAbility()
);
Telescope will use 'parseOnDiskString' and 'toOnDiskString' to serialize and deserialize your object.
Last Words:
- Plz🙏 star ⭐repo.
- Full documentation.
- Examples.
- Static instance of Telescopes?
- it's not recommend because it decreases re-usability of your code, but in some use-cases it's OK to do that🤷🏻.
- Extends from Telescope?
- Why not? TelescopeList actually extends from Telescope
- Under MIT license