When your weekend turns into a two-week native bug safari
It's Friday night. You have a killer app idea, a fresh Flutter or React Native project, and 48 hours before Monday. By Saturday afternoon you're already fighting platform-specific quirks: iOS safe areas, Android back button, keyboard avoidance, permissions, and that one native module that works on one platform but crashes on the other. The dream of a cross-platform MVP turns into a debugging marathon.
The problem isn't the framework — both Flutter and React Native are mature, production-ready tools. The problem is the gap between "write once, run anywhere" and the messy reality of two operating systems with different design languages, lifecycles, and hardware APIs. According to the official Flutter documentation (docs.flutter.dev), widgets are the building blocks, but platform channels and adaptive layouts are where most teams lose time. React Native's docs (reactnative.dev) similarly warn that native modules and platform-specific code are often necessary for full functionality.
The solution? Stop writing boilerplate from scratch. Use well-crafted prompts to generate adaptive UI, navigation, state logic, and native bridges — then spend your weekend on your unique value, not on re-solving the same cross-platform puzzles. This collection gives you 10 battle-tested prompts, from basic screen scaffolding to expert-level native module integration. Each comes with a real example and working code. Let's ship.
1. Basic: Adaptive Screen Scaffold (Flutter)
Task: Generate a screen that respects iOS and Android layout conventions (safe areas, status bar, scroll behavior).
Prompt:
Act as a senior Flutter developer. Create a
HomeScreenwidget that usesSafeArea,Scaffold, and a scrollableListView. Add adaptive padding: 16 on iOS, 12 on Android. UseMediaQueryto detect platform and adjust the AppBar elevation (0 on iOS, 4 on Android). Include a floating action button only on Android. Return complete Dart code with comments.
Example result:
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
final isIOS = Theme.of(context).platform == TargetPlatform.iOS;
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
elevation: isIOS ? 0 : 4,
),
body: SafeArea(
child: ListView.builder(
padding: EdgeInsets.all(isIOS ? 16 : 12),
itemCount: 20,
itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
),
),
floatingActionButton: isIOS
? null
: FloatingActionButton(onPressed: () {}, child: const Icon(Icons.add)),
);
}
}
This prompt saves you from the classic "why does my UI look off on one platform?" trap. Reference: Flutter's official guide on adaptive design (docs.flutter.dev/ui/adaptive).
2. Basic: Navigation with Bottom Tabs (React Native)
Task: Set up a bottom tab navigator with platform-specific icons and a stack for each tab.
Prompt:
You are a React Native expert using React Navigation v6. Generate a
Tab.Navigatorwith three tabs: Home, Search, Profile. Use@react-navigation/bottom-tabsand@react-navigation/native-stack. On iOS, useIoniconswith filled icons; on Android, useMaterialIcons. Provide aNavigationContainerand stack navigators for each tab. Include TypeScript types for the param list.
Example result:
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import Ionicons from 'react-native-vector-icons/Ionicons';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import { Platform } from 'react-native';
type RootTabParamList = { Home: undefined; Search: undefined; Profile: undefined };
const Tab = createBottomTabNavigator<RootTabParamList>();
const Stack = createNativeStackNavigator();
function HomeStack() {
return (
<Stack.Navigator>
<Stack.Screen name="HomeMain" component={HomeScreen} />
</Stack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ color, size }) => {
const iconName = route.name === 'Home' ? 'home' : route.name === 'Search' ? 'search' : 'person';
return Platform.OS === 'ios' ? (
<Ionicons name={iconName} size={size} color={color} />
) : (
<MaterialIcons name={iconName} size={size} color={color} />
);
},
})}
>
<Tab.Screen name="Home" component={HomeStack} />
<Tab.Screen name="Search" component={SearchScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
React Navigation is the de-facto standard (reactnavigation.org). This prompt ensures your navigation feels native on both platforms.
3. Intermediate: State Management with Riverpod (Flutter)
Task: Implement a counter with Riverpod, including async state and error handling.
Prompt:
Write a Flutter app using Riverpod 2.x. Create a
counterProviderusingStateNotifierProvider. Add async loading and error states. Show aCircularProgressIndicatorwhile loading, an error message with retry button on failure, and the counter value with increment/decrement buttons on success. UseConsumerWidgetandref.watch. Provide full code.
Example result:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class CounterState {
final int value;
final bool isLoading;
final String? error;
CounterState({this.value = 0, this.isLoading = false, this.error});
}
class CounterNotifier extends StateNotifier<CounterState> {
CounterNotifier() : super(CounterState());
Future<void> increment() async {
state = CounterState(value: state.value, isLoading: true);
try {
await Future.delayed(const Duration(seconds: 1));
state = CounterState(value: state.value + 1);
} catch (e) {
state = CounterState(value: state.value, error: e.toString());
}
}
}
final counterProvider = StateNotifierProvider<CounterNotifier, CounterState>((ref) => CounterNotifier());
class CounterScreen extends ConsumerWidget {
const CounterScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(counterProvider);
if (state.isLoading) return const Center(child: CircularProgressIndicator());
if (state.error != null) {
return Center(
child: Column(
children: [
Text('Error: ${state.error}'),
ElevatedButton(onPressed: () => ref.read(counterProvider.notifier).increment(), child: const Text('Retry')),
],
),
);
}
return Center(
child: Column(
children: [
Text('Count: ${state.value}'),
ElevatedButton(onPressed: () => ref.read(counterProvider.notifier).increment(), child: const Text('Increment')),
],
),
);
}
}
Riverpod's official docs (riverpod.dev) recommend this pattern for scalable state management.
4. Intermediate: Redux Toolkit with Async Thunks (React Native)
Task: Set up Redux Toolkit with an async API call and loading/error states.
Prompt:
Generate a React Native Redux Toolkit slice for fetching user data from
https://jsonplaceholder.typicode.com/users. UsecreateAsyncThunkandcreateSlice. Handle pending, fulfilled, and rejected states. Provide the store configuration and a component that dispatches the fetch and displays loading/error/data. Use TypeScript.
Example result:
import { createSlice, createAsyncThunk, configureStore } from '@reduxjs/toolkit';
import { Provider, useSelector, useDispatch } from 'react-redux';
export const fetchUsers = createAsyncThunk('users/fetch', async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
return await res.json();
});
const usersSlice = createSlice({
name: 'users',
initialState: { data: [], loading: false, error: null as string | null },
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUsers.pending, (state) => { state.loading = true; state.error = null; })
.addCase(fetchUsers.fulfilled, (state, action) => { state.loading = false; state.data = action.payload; })
.addCase(fetchUsers.rejected, (state, action) => { state.loading = false; state.error = action.error.message || 'Failed'; });
},
});
const store = configureStore({ reducer: { users: usersSlice.reducer } });
export default function App() {
return (
<Provider store={store}>
<UserList />
</Provider>
);
}
function UserList() {
const dispatch = useDispatch();
const { data, loading, error } = useSelector((state: any) => state.users);
if (loading) return <Text>Loading...</Text>;
if (error) return <Text>Error: {error}</Text>;
return <FlatList data={data} keyExtractor={(item) => item.id.toString()} renderItem={({ item }) => <Text>{item.name}</Text>} />;
}
Redux Toolkit is the recommended approach by the Redux team (redux-toolkit.js.org).
5. Advanced: Native Module for Battery Status (Flutter)
Task: Create a Flutter plugin that reads battery level on iOS and Android using platform channels.
Prompt:
Write a Flutter plugin
battery_readerthat exposes a methodgetBatteryLevel(). On Android, useBatteryManager; on iOS, useUIDevice.current.batteryLevel. Provide Dart, Kotlin, and Swift code. Include method channel setup and error handling.
Example result:
Dart:
import 'package:flutter/services.dart';
class BatteryReader {
static const _channel = MethodChannel('battery_reader');
static Future<int> getBatteryLevel() async {
final level = await _channel.invokeMethod<int>('getBatteryLevel');
return level ?? -1;
}
}
Kotlin (Android):
class BatteryReaderPlugin: FlutterPlugin, MethodCallHandler {
private lateinit var channel: MethodChannel
private lateinit var context: Context
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
context = binding.applicationContext
channel = MethodChannel(binding.binaryMessenger, "battery_reader")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "getBatteryLevel") {
val bm = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
result.success(bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY))
} else result.notImplemented()
}
}
Swift (iOS):
import Flutter
import UIKit
public class BatteryReaderPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "battery_reader", binaryMessenger: registrar.messenger())
let instance = BatteryReaderPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "getBatteryLevel" {
UIDevice.current.isBatteryMonitoringEnabled = true
result(Int(UIDevice.current.batteryLevel * 100))
} else { result(FlutterMethodNotImplemented) }
}
}
This follows Flutter's official platform channels guide (docs.flutter.dev/platform-integration/platform-channels).
6. Advanced: Native Module for Device Info (React Native)
Task: Create a native module that returns device model and OS version.
Prompt:
Write a React Native native module
DeviceInfofor Android (Kotlin) and iOS (Swift). ExposegetDeviceInfo()returning{ model, osVersion }. Provide the JavaScript interface and registration inMainApplication.ktandAppDelegate.swift. Use TurboModules if possible.
Example result:
JavaScript:
import { NativeModules } from 'react-native';
const { DeviceInfo } = NativeModules;
export default DeviceInfo;
Android (Kotlin):
class DeviceInfoModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName() = "DeviceInfo"
@ReactMethod
fun getDeviceInfo(promise: Promise) {
val map = Arguments.createMap()
map.putString("model", Build.MODEL)
map.putString("osVersion", Build.VERSION.RELEASE)
promise.resolve(map)
}
}
iOS (Swift):
@objc(DeviceInfo)
class DeviceInfo: NSObject {
@objc func getDeviceInfo(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
resolve(["model": UIDevice.current.model, "osVersion": UIDevice.current.systemVersion])
}
}
React Native's native modules documentation (reactnative.dev/docs/native-modules-android) is your reference.
7. Expert: CI/CD for Two Platforms (GitHub Actions)
Task: Set up a CI pipeline that builds both Android APK and iOS IPA on every push.
Prompt:
Create a GitHub Actions workflow for a Flutter project. It should run on
ubuntu-latestfor Android andmacos-latestfor iOS. Cache Flutter SDK, run tests, build APK (debug) and iOS (no codesign). Upload artifacts. Provide complete YAML.
Example result:
name: Flutter CI
on: push
jobs:
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with: { flutter-version: '3.24.0' }
- run: flutter pub get
- run: flutter test
- run: flutter build apk --debug
- uses: actions/upload-artifact@v4
with: { name: android-apk, path: build/app/outputs/flutter-apk/app-debug.apk }
ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with: { flutter-version: '3.24.0' }
- run: flutter pub get
- run: flutter build ios --no-codesign
This pattern is widely used; see Flutter's official CI docs (docs.flutter.dev/deployment/cd).
8. Expert: E2E Testing with Detox (React Native)
Task: Write a Detox test that checks login flow and navigation.
Prompt:
Generate a Detox test for a React Native app. Test: launch app, tap on email field, type 'test@example.com', tap password field, type 'password123', tap login button, expect to see 'Welcome' text. Provide
detox.config.jsand the test file. Use Jest.
Example result:
// e2e/firstTest.e2e.js
describe('Login flow', () => {
beforeAll(async () => { await device.launchApp(); });
it('should login successfully', async () => {
await element(by.id('email')).typeText('test@example.com');
await element(by.id('password')).typeText('password123');
await element(by.id('loginButton')).tap();
await expect(element(by.text('Welcome'))).toBeVisible();
});
});
Detox is the recommended E2E framework for React Native (wix.github.io/Detox).
9. Expert: Performance Profiling Prompt
Task: Identify and fix jank in a Flutter list.
Prompt:
Act as a Flutter performance expert. Given a
ListViewwith complex items, suggest optimizations: useListView.builder,constconstructors,RepaintBoundary, and avoidOpacitywidgets. Provide before/after code and explain how to use DevTools to measure frame times.
Example result:
Before:
ListView(children: items.map((e) => Opacity(opacity: 0.9, child: ComplexItem(e))).toList())
After:
ListView.builder(
itemCount: items.length,
itemBuilder: (context, i) => RepaintBoundary(child: const ComplexItem()),
)
Use flutter run --profile and DevTools timeline. Reference: Flutter performance best practices (docs.flutter.dev/perf/best-practices).
10. Expert: Cross-Platform Design System
Task: Create a theme that adapts to iOS and Android.
Prompt:
Generate a Flutter
ThemeDatathat uses Cupertino colors on iOS and Material 3 on Android. Define text styles, button styles, and input decorations per platform. Provide aThemeProviderthat switches based ondefaultTargetPlatform. Include code.
Example result:
ThemeData getTheme(BuildContext context) {
final isIOS = defaultTargetPlatform == TargetPlatform.iOS;
return isIOS
? ThemeData(
primaryColor: CupertinoColors.activeBlue,
textTheme: CupertinoTextThemeData(),
pageTransitionsTheme: PageTransitionsTheme(builders: { TargetPlatform.iOS: CupertinoPageTransitionsBuilder() }),
)
: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.blue,
pageTransitionsTheme: PageTransitionsTheme(builders: { TargetPlatform.android: ZoomPageTransitionsBuilder() }),
);
}
This ensures a native feel on both platforms. See Material 3 and Cupertino docs.
Ship your MVP this weekend
Cross-platform development doesn't have to be a bug-ridden nightmare. With these prompts, you can generate adaptive UI, robust state management, native bridges, and CI pipelines in minutes — leaving you time to focus on your app's unique features. Remember: the goal isn't to avoid native code entirely, but to write it efficiently when needed.
Ready to put these prompts to work? Try them in your next Flutter or React Native project, and share your results. For more AI-powered development guides, explore asibiont.com/blog.
Comments