flutter_on_rails 0.0.7-alpha
flutter_on_rails: ^0.0.7-alpha copied to clipboard
Flutter on rails is the fastest way to bridge your web app with a Flutter-powered for cross plateform mobile and desktop app with ease and minimal changes, maximum freedom
Flutter on Rails #
A powerful integration between Flutter and Ruby on Rails that enables seamless communication between web and mobile applications.
Features #
- 🔄 Bi-directional Communication: Seamless communication between Flutter and Rails
- 📱 Cross-Platform: Works on iOS, Android, and desktop
- 🎨 Customizable UI: Platform-specific dialogs,navigation and animations
- 🔌 Easy Integration: Simple setup and configuration
- 🚀 Performance Optimized: Efficient WebView handling and state management
Installation #
Add the package to your pubspec.yaml
:
dependencies:
flutter_on_rails: ^0.0.7.alpha
Quick Start #
- Initialize the WebView in your Flutter app:
import 'package:flutter/material.dart';
import 'package:flutter_on_rails/flutter_on_rails.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await init();
runApp(MaterialApp(home: MainScreen()));
}
- Add navigation attributes to your Rails views:
<a
href="/messages/new"
data_frails_navigation='{"action":"push", "navigable":true, "animate":"circularReveal","backgroundColor": "#111827","leadingColor": "#111827","title": "Message" }'
>New Message</a
>
API Reference #
Navigation #
The package supports both regular and animated navigation between screens. You can use the following attributes in your Rails views:
Regular Navigation
<a
href="/messages/new"
data_frails_navigation='{"action":"push", "navigable":true}'
>New Message</a
>
Animated Navigation
<a
href="/messages/new"
data_frails_navigation='{
"action": "push",
"navigable": true,
"animate": "circularReveal",
"backgroundColor": "#111827",
"leadingColor": "#111827",
"title": "Message"
}'
>New Message</a
>
Navigation Options
-
Regular Navigation Properties:
action
: "push" or "pop"navigable
: true/false
-
Animated Navigation Properties:
action
: "push" or "pop"navigable
: true/falseanimate
: Animation type (see below)backgroundColor
: Background color of the new screenleadingColor
: Color of the leading widget (e.g., AppBar)title
: Title of the new screen
-
Available Animation Types:
none
: No animationrotate
: Rotating transitionscale
: Scaling transitionright
: Slide from rightleft
: Slide from leftdownToUp
: Slide from bottom to toptopToDown
: Slide from top to bottomcircularReveal
: Circular reveal animationfade
: Fade transition
Example Usage
- Simple Push Navigation:
<a href="/products" data_frails_navigation='{"action":"push", "navigable":true}'
>View Products</a
>
- Animated Push Navigation:
<a
href="/products"
data_frails_navigation='{
"action": "push",
"navigable": true,
"animate": "fade",
"backgroundColor": "#FFFFFF",
"leadingColor": "#2196F3",
"title": "Products"
}'
>View Products</a
>
- Pop Navigation with Animation:
<button
data_frails_navigation='{
"action": "pop",
"navigable": true,
"animate": "left"
}'
>
Go Back
</button>
Navigation Best Practices #
- Always provide a fallback for non-Flutter environments
- Use appropriate animation types based on the context
- Keep navigation configurations consistent across your application
- Test navigation on both iOS and Android
- Consider the user experience when choosing animation types
- Use semantic HTML elements for navigation links
- Provide clear visual feedback for navigation actions
Dialog Options #
{
'title': String, // Dialog title
'content': String, // Dialog content
'animate': String, // Animation type
'actions': List<Map>, // Dialog actions
'backgroundColor': Color, // Dialog background color
'elevation': double, // Dialog elevation
'shape': ShapeBorder, // Dialog shape
'borderRadius': double, // Dialog border radius
'titlePadding': EdgeInsets,// Title padding
'contentPadding': EdgeInsets,// Content padding
'actionsPadding': EdgeInsets,// Actions padding
'buttonPadding': EdgeInsets,// Button padding
'insetPadding': EdgeInsets,// Dialog inset padding
}
Dialog Animations #
The package provides various animation options for dialogs to enhance the user experience. Here's how to use them:
Basic Dialog with Animation
const dialogConfig = {
component: "dialog",
title: "Custom Dialog",
content: "This is a test message",
animate: "scale", // Animation type
actions: [
{
text: "OK",
onPressed: () => console.log("OK pressed"),
},
],
};
Available Dialog Animation Types
- rotate: Rotating transition
- scale: Scaling transition
- right: Slide from right
- left: Slide from left
- downToUp: Slide from bottom to top
- topToDown: Slide from top to bottom
- none: No animation
Dialog Configuration Options
const dialogConfig = {
component: "dialog",
title: "Dialog Title",
content: "Dialog content",
animate: "scale", // Animation type
backgroundColor: "#FFFFFF", // Dialog background color
elevation: 24.0, // Dialog elevation
shape: "rounded", // Dialog shape
borderRadius: 8.0, // Border radius
titlePadding: "16.0,24.0,16.0,24.0", // Title padding
contentPadding: "24.0,24.0,24.0,24.0", // Content padding
actionsPadding: "8.0,8.0,8.0,8.0", // Actions padding
buttonPadding: "8.0,16.0,8.0,16.0", // Button padding
insetPadding: "40.0,40.0,40.0,40.0", // Dialog inset padding
actions: [
{
text: "Cancel",
onPressed: () => console.log("Cancel pressed"),
style: {
color: "#FF0000",
fontSize: 16.0,
},
},
{
text: "Confirm",
onPressed: () => console.log("Confirm pressed"),
style: {
color: "#2196F3",
fontSize: 16.0,
},
},
],
};
Example Usage
- Simple Dialog with Scale Animation:
const simpleDialog = {
component: "dialog",
title: "Welcome",
content: "Welcome to our app!",
animate: "scale",
actions: [
{
text: "Got it",
onPressed: () => console.log("Got it pressed"),
},
],
};
- Dialog with Custom Styling:
const styledDialog = {
component: "dialog",
title: "Delete Item",
content: "Are you sure you want to delete this item?",
animate: "downToUp",
backgroundColor: "#FFFFFF",
elevation: 24.0,
borderRadius: 12.0,
actions: [
{
text: "Cancel",
onPressed: () => console.log("Cancel pressed"),
style: {
color: "#666666",
},
},
{
text: "Delete",
onPressed: () => console.log("Delete pressed"),
style: {
color: "#FF0000",
},
},
],
};
- Dialog with Custom Padding:
const paddedDialog = {
component: "dialog",
title: "Settings",
content: "Configure your settings",
animate: "right",
titlePadding: "16.0,24.0,16.0,24.0",
contentPadding: "24.0,24.0,24.0,24.0",
actionsPadding: "8.0,8.0,8.0,8.0",
actions: [
{
text: "Save",
onPressed: () => console.log("Save pressed"),
},
],
};
- Programmatic Dialog Creation:
function showDialog(options) {
if (window.flutter_on_rails) {
const defaultOptions = {
animate: "scale",
backgroundColor: "#FFFFFF",
elevation: 24.0,
};
const dialogConfig = {
component: "dialog",
...defaultOptions,
...options,
};
window.flutter_on_rails.callHandler(
"Flutter",
JSON.stringify(dialogConfig)
);
}
}
// Usage
showDialog({
title: "Custom Dialog",
content: "This is a test message",
animate: "rotate",
actions: [
{
text: "OK",
onPressed: () => console.log("OK pressed"),
},
],
});
Dialog Best Practices
- Choose appropriate animation types based on the dialog's purpose
- Keep dialog content concise and clear
- Use consistent styling across your application
- Provide clear action buttons
- Consider the user's context when choosing animation types
- Test dialogs on both iOS and Android
- Handle edge cases (e.g., long content, multiple actions)
- Provide proper error handling for dialog actions
- Consider accessibility when choosing colors and animations
- Use appropriate padding and spacing for better readability
Platform-Specific Features #
iOS #
- Custom keyboard handling
- Native dialog support
- Gesture navigation
- Pull-to-refresh functionality
Android #
-
Custom WebView settings
-
Native dialog support
-
Pull-to-refresh functionality
-
HTTP Traffic Configuration: Android prioritizes data security by default. Using HTTP (which is not encrypted) can expose data to eavesdropping, especially over public Wi-Fi networks.
For apps targeting API level 28 or higher (Android 9 Pie and later), cleartext HTTP traffic is disabled by default. If your app needs to use cleartext HTTP, you can enable it by:
- Adding to AndroidManifest.xml:
<application android:usesCleartextTraffic="true" ...>
- Or configuring in network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true"> <trust-anchors> <certificates src="system" /> </trust-anchors> </base-config> </network-security-config>
Note: It's recommended to use HTTPS whenever possible for secure communication.
JavaScript API Integration #
The Flutter on Rails package provides a powerful JavaScript API that allows you to communicate with your Flutter app from your Rails views. Here's how to use it:
Basic Setup #
- Import the Stimulus controller in your Rails application:
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
connect() {
// Initialize communication with Flutter
if (window.flutter_on_rails) {
window.flutter_on_rails.callHandler(
"Flutter",
JSON.stringify({
// Your payload here
})
);
}
}
}
Widget Parser API #
The widget parser allows you to create Flutter widgets using JSON objects. Here's an example of creating an AppBar:
const appBarConfig = {
type: "AppBar",
backgroundColor: "#4179AF",
title: {
type: "Text",
data: "My App",
style: {
color: "#1B2834",
fontSize: 20.0,
},
},
actions: [
{
type: "Container",
width: 40.0,
height: 40.0,
margin: "10.0,0,10.0,0",
child: {
type: "ElevatedButton",
color: "ff2196f3",
click_event: "route:/productDetail?goods_id=123",
child: {
type: "Icon",
data: "list",
size: 25.0,
},
},
},
],
};
Available Components #
1. Dialog
const dialogConfig = {
component: "dialog",
title: "Flutter on Rails",
content: "Welcome to Flutter on rails !!!",
animate: "left", // Optional: none, rotate, scale, right, left, downToUp, topToDown
};
2. Bottom Sheet
const bottomSheetConfig = {
component: "bottomsheet",
title: "Bottomsheet",
content: "My bottomsheet create with json object",
};
3. License Dialog
const licenseConfig = {
component: "licence",
title: "My mobile app",
content: "Read the detail below about my application",
};
4. Share
const shareConfig = {
component: "share",
title: "Check my awesome app here",
content: "Check my awesome app here https://www.github.com/AdamMusa",
};
5. Snackbar
const snackbarConfig = {
component: "snackbar",
title: "Login successfully",
content: "My snackbar component from the web",
};
Communication Methods #
- Sending Data to Flutter:
sendDataToFlutter() {
if (window.flutter_on_rails) {
const payload = {
// Your configuration here
};
window.flutter_on_rails.callHandler('Flutter', JSON.stringify(payload));
}
}
- Receiving Data from Flutter:
receiveDataFromFlutter() {
window.receiveDataFromFlutter = function (data) {
// Handle the received data
console.log(`Received from Flutter: ${data}`);
};
}
Widget Types #
The widget parser supports various Flutter widgets:
- Container
- Properties: width, height, margin, padding, color, child
- Text
- Properties: data, style (color, fontSize)
- Icon
- Properties: data, size, color
- ElevatedButton
- Properties: color, click_event, child, padding
- AppBar
- Properties: backgroundColor, title, actions
Supported Widgets #
The widget parser supports a wide range of Flutter widgets. Here's a comprehensive list of supported widgets and their properties:
Layout Widgets
- Container
{
type: "Container",
width: 100.0,
height: 100.0,
margin: "10.0,10.0,10.0,10.0",
padding: "10.0,10.0,10.0,10.0",
color: "#FFFFFF",
decoration: {
borderRadius: 8.0,
borderColor: "#000000",
borderWidth: 1.0
},
child: {
// Child widget configuration
}
}
- Row & Column
{
type: "Row", // or "Column"
mainAxisAlignment: "start", // start, end, center, spaceBetween, spaceAround, spaceEvenly
crossAxisAlignment: "center", // start, end, center, stretch, baseline
mainAxisSize: "max", // min, max
children: [
// Array of child widgets
]
}
- Stack
{
type: "Stack",
alignment: "topLeft", // topLeft, topCenter, topRight, centerLeft, center, centerRight, bottomLeft, bottomCenter, bottomRight
fit: "loose", // loose, expand, passthrough
children: [
// Array of child widgets
]
}
- Wrap
{
type: "Wrap",
direction: "horizontal", // horizontal, vertical
alignment: "start", // start, end, center, spaceBetween, spaceAround, spaceEvenly
spacing: 8.0,
runSpacing: 8.0,
children: [
// Array of child widgets
]
}
Scrolling Widgets
- ListView
{
type: "ListView",
scrollDirection: "vertical", // vertical, horizontal
padding: "10.0,10.0,10.0,10.0",
children: [
// Array of child widgets
]
}
- GridView
{
type: "GridView",
crossAxisCount: 2,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 1.0,
children: [
// Array of child widgets
]
}
- SingleChildScrollView
{
type: "SingleChildScrollView",
scrollDirection: "vertical", // vertical, horizontal
padding: "10.0,10.0,10.0,10.0",
child: {
// Child widget configuration
}
}
- PageView
{
type: "PageView",
scrollDirection: "horizontal", // horizontal, vertical
children: [
// Array of child widgets
]
}
Basic Widgets
- Text
{
type: "Text",
data: "Hello World",
style: {
color: "#000000",
fontSize: 16.0,
fontWeight: "normal", // normal, bold, w100-w900
fontStyle: "normal", // normal, italic
textAlign: "left", // left, right, center, justify, start, end
overflow: "clip" // clip, fade, ellipsis, visible
}
}
- SelectableText
{
type: "SelectableText",
data: "Selectable Text",
style: {
color: "#000000",
fontSize: 16.0
}
}
- Icon
{
type: "Icon",
data: "home", // Material icon name
size: 24.0,
color: "#000000"
}
- Image
{
type: "Image",
src: "https://example.com/image.jpg",
width: 100.0,
height: 100.0,
fit: "cover", // fill, contain, cover, fitWidth, fitHeight, none, scaleDown
alignment: "center"
}
Layout Helpers
- Padding
{
type: "Padding",
padding: "10.0,10.0,10.0,10.0",
child: {
// Child widget configuration
}
}
- Align
{
type: "Align",
alignment: "center", // topLeft, topCenter, topRight, centerLeft, center, centerRight, bottomLeft, bottomCenter, bottomRight
child: {
// Child widget configuration
}
}
- Center
{
type: "Center",
child: {
// Child widget configuration
}
}
- Expanded
{
type: "Expanded",
flex: 1,
child: {
// Child widget configuration
}
}
- SizedBox
{
type: "SizedBox",
width: 100.0,
height: 100.0,
child: {
// Optional child widget configuration
}
}
Special Widgets
- Card
{
type: "Card",
elevation: 2.0,
margin: "10.0,10.0,10.0,10.0",
shape: "rounded", // rounded, beveled, stadium
color: "#FFFFFF",
child: {
// Child widget configuration
}
}
- ListTile
{
type: "ListTile",
leading: {
// Widget configuration for leading icon/button
},
title: {
type: "Text",
data: "Title"
},
subtitle: {
type: "Text",
data: "Subtitle"
},
trailing: {
// Widget configuration for trailing icon/button
}
}
- Divider
{
type: "Divider",
height: 1.0,
color: "#BDBDBD",
thickness: 1.0,
indent: 0.0,
endIndent: 0.0
}
- DropCapText
{
type: "DropCapText",
text: "Your text here",
dropCap: {
width: 50.0,
height: 50.0,
style: {
fontSize: 30.0,
color: "#000000"
}
}
}
Utility Widgets
- SafeArea
{
type: "SafeArea",
child: {
// Child widget configuration
}
}
- ClipRRect
{
type: "ClipRRect",
borderRadius: 8.0,
child: {
// Child widget configuration
}
}
- Opacity
{
type: "Opacity",
opacity: 0.5,
child: {
// Child widget configuration
}
}
- AspectRatio
{
type: "AspectRatio",
aspectRatio: 16.0 / 9.0,
child: {
// Child widget configuration
}
}
Widget Best Practices #
- Use appropriate widget types for specific use cases
- Maintain consistent styling across your application
- Consider platform-specific behaviors
- Optimize widget trees for performance
- Use proper padding and margins
- Handle widget states appropriately
- Consider accessibility requirements
- Test widgets on different screen sizes
- Use semantic widget names
- Follow Flutter's widget composition patterns
Error Handling #
Always check for the availability of the Flutter handler:
if (window.flutter_on_rails) {
// Your code here
} else {
console.error("Flutter InAppWebView handler not available");
}
Best Practices #
- Always validate your JSON payload before sending
- Use proper error handling
- Keep your widget configurations modular and reusable
- Test your configurations in both development and production environments
- Use TypeScript for better type safety if possible
For more examples and detailed documentation, check out the example directory in the repository.
Contributing #
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Support #
For support, email adammusaaly@gmail.com or join our Slack channel.
Acknowledgments #
- Flutter team for the amazing framework
- Ruby on Rails community
- All contributors who have helped shape this project
Made with ❤️ by [Adam Moussa Ali]