flutter_post_frame 0.0.3
flutter_post_frame: ^0.0.3 copied to clipboard
A lightweight Flutter package that provides utilities to execute callbacks after the first frame is rendered. Perfect for getting widget sizes or performing post-build operations.
flutter_post_frame #
A lightweight Flutter package that provides utilities to execute callbacks after the first frame is rendered. Perfect for getting widget sizes, managing multiple scroll controllers, or performing post-build operations.
Example #

Features #
- FlutterPostFrameMixin: A mixin for
StatefulWidgetstates that callsonAfterBuildFrame()after the first frame is rendered. - PostFrameBuilder: A widget wrapper that provides a callback with the
BuildContextand rendered widget'sSizeafter the first frame. - PostFrameScrollBuilder: A widget that manages multiple
ScrollControllers with post-frame callbacks. - FlutterPostFrameScrollMixin: A mixin for managing multiple scroll controllers with post-frame callbacks.
- PostFrameScrollManager: A utility class for creating and managing multiple named scroll controllers.
Getting started #
Add the package to your pubspec.yaml:
dependencies:
flutter_post_frame: ^0.0.1
Then run:
flutter pub get
Usage #
Using FlutterPostFrameMixin #
Add the mixin to your State class to execute code after the first frame:
import 'package:flutter_post_frame/flutter_post_frame.dart';
class MyWidgetState extends State<MyWidget> with FlutterPostFrameMixin<MyWidget> {
@override
FutureOr<void> onAfterBuildFrame(BuildContext context) {
// This runs after the first frame is rendered
print('Widget has been rendered!');
}
@override
Widget build(BuildContext context) {
return Container();
}
}
Using PostFrameBuilder #
Wrap your widget with PostFrameBuilder to get the widget's size after rendering:
import 'package:flutter_post_frame/post_frame_builder.dart';
PostFrameBuilder(
onAfterBuildFrame: (context, size) {
print('Widget size: $size');
},
child: Container(
width: 200,
height: 100,
color: Colors.blue,
),
)
initState-like behavior in StatelessWidget #
Use PostFrameBuilder to execute one-time initialization logic in a StatelessWidget, similar to initState in StatefulWidget:
import 'package:flutter_post_frame/post_frame_builder.dart';
class MyStatelessWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return PostFrameBuilder(
onAfterBuildFrame: (context, size) {
// This runs once after the first frame - like initState!
// Perfect for:
// - Showing a dialog or snackbar
// - Triggering navigation
// - Fetching data based on widget size
// - Starting animations
print('Widget initialized with size: $size');
},
child: YourContentWidget(),
);
}
}
This is especially useful when you want to keep your widget stateless but still need to perform post-build operations.
Using PostFrameScrollBuilder (Multiple ScrollControllers) #
Manage multiple scroll controllers with ease:
import 'package:flutter_post_frame/flutter_post_frame.dart';
PostFrameScrollBuilder(
controllerKeys: ['list1', 'list2', 'list3'],
initialScrollOffsets: {'list1': 100.0}, // Optional initial offsets
onAfterBuildFrame: (data) {
// Access BuildContext, Size, and scroll manager
print('Widget size: ${data.size}');
print('Controllers: ${data.scrollManager.keys}');
// Scroll to a position after build
data.scrollManager.animateTo(
'list1',
200,
duration: Duration(milliseconds: 300),
);
},
onScroll: (key, offset) {
// Track scroll position changes for any controller
print('$key scrolled to: $offset');
},
builder: (context, scrollManager) {
return Row(
children: [
Expanded(
child: ListView.builder(
controller: scrollManager['list1'],
itemBuilder: (context, index) => Text('Item $index'),
),
),
Expanded(
child: ListView.builder(
controller: scrollManager['list2'],
itemBuilder: (context, index) => Text('Item $index'),
),
),
],
);
},
)
Using FlutterPostFrameScrollMixin #
For full control in StatefulWidgets:
import 'package:flutter_post_frame/flutter_post_frame.dart';
class MyWidgetState extends State<MyWidget>
with FlutterPostFrameScrollMixin<MyWidget> {
@override
List<String> get scrollControllerKeys => ['mainList', 'sidebar'];
@override
Map<String, double>? get initialScrollOffsets => {'mainList': 50.0};
@override
FutureOr<void> onAfterBuildFrameWithScroll(PostFrameScrollData data) {
// Access BuildContext, Size, and scroll manager
print('Widget size: ${data.size}');
// Add scroll listeners
scrollManager.addScrollListener('mainList', () {
print('Scroll offset: ${scrollManager.getScrollOffset('mainList')}');
});
}
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: ListView(
controller: scrollManager['mainList'],
children: [...],
),
),
SizedBox(
width: 200,
child: ListView(
controller: scrollManager['sidebar'],
children: [...],
),
),
],
);
}
}
PostFrameScrollManager API #
The PostFrameScrollManager class provides these methods:
final manager = PostFrameScrollManager();
// Create controllers
manager.createController('key', initialScrollOffset: 0.0);
// Access controllers
final controller = manager['key']; // Or manager.getController('key')
// Scroll operations
manager.animateTo('key', 100, duration: Duration(ms: 300));
manager.jumpTo('key', 100);
manager.scrollToTop('key');
manager.scrollToBottom('key');
// Get scroll information
manager.getScrollOffset('key');
manager.getMaxScrollExtent('key');
manager.getMinScrollExtent('key');
// Listeners
manager.addScrollListener('key', callback);
manager.removeScrollListener('key', callback);
// Cleanup
manager.disposeController('key');
manager.dispose(); // Dispose all
Supported Platforms #
| Android | iOS | Linux | macOS | Web | Windows |
|---|---|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Additional information #
This package uses WidgetsBinding.instance.endOfFrame to ensure callbacks are executed after the widget tree has been fully rendered. This is particularly useful when you need to:
- Get the actual rendered size of a widget
- Perform navigation or show dialogs after the initial build
- Execute animations or scroll operations that depend on layout completion
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
License #
This project is licensed under the MIT License - see the LICENSE file for details.