Skip to content

Commit

Permalink
feat: new example app
Browse files Browse the repository at this point in the history
  • Loading branch information
drochetti committed Nov 7, 2023
1 parent 4f34ac8 commit db31db5
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 59 deletions.
5 changes: 3 additions & 2 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# fal_sample_app

A new Flutter project.
A sample app that shows how to use https://fal.ai model APIs with Flutter.

## Getting Started

This project is a starting point for a Flutter application.
This project is a starting point for a Flutter application with fal.ai.

A few resources to get you started if this is your first Flutter project:

- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
- [Get started with fal.ai](https://fal.ai/docs)

For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
Expand Down
2 changes: 2 additions & 0 deletions example/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,7 @@
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) need access to your photo gallery so you can pick image patterns</string>
</dict>
</plist>
118 changes: 71 additions & 47 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:fal_client/fal_client.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

import 'types.dart';

Expand All @@ -11,19 +12,20 @@ import 'types.dart';
final fal = FalClient.withCredentials('FAL_KEY_ID:FAL_KEY_SECRET');

void main() {
runApp(const MyApp());
runApp(const FalSampleApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});
class FalSampleApp extends StatelessWidget {
const FalSampleApp({super.key});

// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'fal.ai',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.indigo, brightness: Brightness.dark),
useMaterial3: true,
),
home: const TextoToImageScreen(title: 'fal.ai'),
Expand All @@ -33,73 +35,95 @@ class MyApp extends StatelessWidget {

class TextoToImageScreen extends StatefulWidget {
const TextoToImageScreen({super.key, required this.title});

// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.

// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked 'final'.

final String title;

@override
State<TextoToImageScreen> createState() => _TextoToImageScreenState();
}

class _TextoToImageScreenState extends State<TextoToImageScreen> {
bool _isLoading = false;
final _promptController =
TextEditingController(text: 'a cute shih-tzu puppy');
ImageRef? _image;
final ImagePicker _picker = ImagePicker();
XFile? _image;
final TextEditingController _promptController = TextEditingController();
String? _generatedImageUrl;
bool _isProcessing = false;

void _generateImage() async {
Future<String> generateImage(XFile image, String prompt) async {
final result = await fal.subscribe(textToImageId, input: {
'prompt': prompt,
'image_url': image,
});
return result['image']['url'] as String;
}

void _onGenerateImage() async {
if (_image == null || _promptController.text.isEmpty) {
// Handle error: either image not selected or prompt not entered
return;
}
setState(() {
_isLoading = true;
_image = null;
_isProcessing = true;
});
final result = await fal.subscribe(textToImageId,
input: {
'prompt': _promptController.text,
'model_name': 'stabilityai/stable-diffusion-xl-base-1.0',
'image_size': 'square_hd'
},
onQueueUpdate: (update) => {print(update)});
String imageUrl = await generateImage(_image!, _promptController.text);
setState(() {
_isLoading = false;
_image = TextToImageResult.fromMap(result).images[0];
_generatedImageUrl = imageUrl;
_isProcessing = false;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
title: const Text('Illusion Diffusion'),
),
body: Center(
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextField(controller: _promptController),
if (_isLoading) const CircularProgressIndicator(),
if (!_isLoading && _image != null)
FittedBox(
fit: BoxFit.fill,
child: Image.network(_image!.url,
width: _image!.width.toDouble(),
height: _image!.height.toDouble()),
),
ElevatedButton(
onPressed: () async {
final XFile? image =
await _picker.pickImage(source: ImageSource.gallery);
setState(() {
_image = image;
});
},
child: const Text('Pick Image'),
),
// if (_image != null)
// Image,
TextFormField(
controller: _promptController,
decoration: const InputDecoration(labelText: 'Imagine...'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _isProcessing ? null : _onGenerateImage,
child: _isProcessing
? const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(),
),
SizedBox(width: 8),
Text('Generating...'),
],
)
: const Text('Generate Image'),
),
if (_generatedImageUrl != null)
Padding(
padding: const EdgeInsets.only(top: 20),
child: Image.network(_generatedImageUrl!)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _generateImage,
tooltip: 'Generate',
child: const Icon(Icons.play_arrow_rounded),
),
);
}
}
16 changes: 7 additions & 9 deletions example/lib/types.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
class TextToImageResult {
final List<ImageRef> images;
class IllusionDiffusionResult {
final ImageRef image;
final int seed;

TextToImageResult({required this.images, required this.seed});
IllusionDiffusionResult({required this.image, required this.seed});

factory TextToImageResult.fromMap(Map<String, dynamic> json) {
return TextToImageResult(
images: (json['images'] as List<dynamic>)
.map((e) => ImageRef.fromMap(e as Map<String, dynamic>))
.toList(),
factory IllusionDiffusionResult.fromMap(Map<String, dynamic> json) {
return IllusionDiffusionResult(
image: ImageRef.fromMap(json['image'] as Map<String, dynamic>),
seed: (json['seed'] * 1).round(),
);
}
Expand All @@ -30,4 +28,4 @@ class ImageRef {
}
}

const textToImageId = '110602490-lora';
const textToImageId = '54285744-illusion-diffusion';
2 changes: 2 additions & 0 deletions example/macos/Runner/DebugProfile.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
</plist>
2 changes: 2 additions & 0 deletions example/macos/Runner/Release.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
</plist>
2 changes: 1 addition & 1 deletion example/test/widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import 'package:fal_sample_app/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
await tester.pumpWidget(const FalSampleApp());

// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
Expand Down

0 comments on commit db31db5

Please sign in to comment.