Connect FlutterFlow to WordPress using the WordPress REST API at /wp-json/wp/v2/. Public content reads (posts, pages, media) work as direct API Calls with no authentication — ideal for headless blog or news apps. Authenticated writes use Application Passwords proxied through Firebase Cloud Functions to keep credentials out of your app bundle.
| Fact | Value |
|---|---|
| Tool | WordPress |
| Category | Media & Content |
| Method | FlutterFlow API Call |
| Difficulty | Beginner |
| Time required | 30 minutes |
| Last updated | July 2026 |
Build a Headless WordPress App in FlutterFlow
WordPress ships a first-party REST API out of the box — no plugin required. Every WordPress site exposes posts, pages, media, categories, tags, and custom post types at /wp-json/wp/v2/, and public reads require zero authentication. That means you can build a fully functional headless news or blog app in FlutterFlow without writing a single line of backend code for the read path. The ?_embed query parameter is the key detail: it inlines the featured image and author data into the same post response, so your post card list loads in one round trip instead of making separate media requests per item.
The split comes when you need writes. WordPress uses Application Passwords (introduced in WordPress 5.6) — per-app tokens used as HTTP Basic Auth credentials. Since FlutterFlow compiles to a Flutter client running on the user's device, any secret placed in an API Call header is embedded in the app bundle and can be extracted. Application Passwords must be proxied through a backend like Firebase Cloud Functions or a Supabase Edge Function. The function holds the credential server-side and forwards authenticated requests to WordPress on behalf of the app.
One gotcha to check early: some managed hosts and WordPress security plugins (Wordfence, iThemes Security) disable or restrict the /wp-json endpoint, returning 401 or 404 even for public posts. Before you build, confirm the endpoint is reachable by visiting https://yoursite.com/wp-json/wp/v2/posts in a browser. WordPress is free and open-source to self-host; hosting costs vary by provider.
Integration method
FlutterFlow connects to WordPress through its built-in REST API at /wp-json/wp/v2/. Public reads (posts, pages, categories, media) are direct API Calls requiring no secret key — making a read-only news or blog app genuinely beginner-level. Authenticated write operations (create/update/delete posts) use Application Passwords as HTTP Basic Auth, which must be routed through a Firebase Cloud Function or Supabase Edge Function to keep the credential out of the compiled app.
Prerequisites
- A WordPress site (self-hosted or WordPress.com Business plan) with /wp-json/wp/v2/ accessible — verify in a browser before starting
- A FlutterFlow project with at least one screen to display the content feed
- For write operations: a WordPress user account with Editor or Administrator role to generate an Application Password
- For write operations: a Firebase project (Blaze plan) or a Supabase project to host the proxy Cloud Function / Edge Function
- Basic familiarity with FlutterFlow's API Calls panel and Data Types
Step-by-step guide
Verify the WordPress REST API is accessible
Before building anything in FlutterFlow, confirm that your WordPress site's REST API is publicly reachable. Open a browser and navigate to https://yoursite.com/wp-json/wp/v2/posts — you should see a JSON array of recent posts. If you get a 401 Unauthorized or 404 Not Found, you have a configuration issue to resolve first. Common causes: (1) a security plugin like Wordfence or iThemes Security has disabled REST API access for non-logged-in users — you'll need to whitelist the endpoint in the plugin settings; (2) the site is behind HTTP Auth (common on staging environments) — add the HTTP Auth credentials to every API Call header; (3) Pretty Permalinks are disabled in WordPress Settings → Permalinks, which breaks /wp-json routing — switch to any permalink structure other than 'Plain'. Once you can see the raw JSON in the browser, you're ready to build the API Group in FlutterFlow. Make a note of your site's base URL — you'll use it as the API Group base URL in the next step.
Pro tip: Add ?per_page=1 to the URL (e.g., /wp-json/wp/v2/posts?per_page=1) for a faster test — it fetches only the most recent post.
Expected result: Your browser displays a JSON array containing at least one post object with fields like id, title, content, excerpt, and _links.
Create an API Group in FlutterFlow for the WordPress REST API
In FlutterFlow, click API Calls in the left navigation panel. Click the + Add button and choose Create API Group. Name the group 'WordPress' and set the Base URL to https://yoursite.com/wp-json/wp/v2 — replace 'yoursite.com' with your actual WordPress domain. Leave authentication as 'None' for now (public reads require no auth header). Click Save to create the group. Next, add your first API Call inside this group: click + Add API Call, name it 'Get Posts', set the method to GET, and enter /posts as the endpoint path. In the Query Parameters tab, click + Add Parameter and add two parameters: (1) name '_embed', value '1' — this tells WordPress to include the featured media and author in the response so you avoid extra requests per post; (2) name 'per_page', type Integer, default '10' — controls how many posts are returned. Click Response & Test, paste a sample posts response from your browser test into the Response Body field, then click Get Reponse Fields to auto-generate JSON paths. FlutterFlow will extract paths like $.title.rendered, $.excerpt.rendered, $.id, and nested paths like $._embedded['wp:featuredmedia'][0].source_url for the featured image. Map these into a Data Type called 'WPPost' with fields: id (Integer), title (String), excerpt (String), featuredImageUrl (String), content (String). Click Test API Call to confirm it returns live data.
1{2 "Group Name": "WordPress",3 "Base URL": "https://yoursite.com/wp-json/wp/v2",4 "Calls": [5 {6 "Name": "Get Posts",7 "Method": "GET",8 "Endpoint": "/posts",9 "Query Params": {10 "_embed": "1",11 "per_page": "[variable:perPage]",12 "page": "[variable:page]",13 "categories": "[variable:categoryId]"14 },15 "JSON Paths": {16 "id": "$[*].id",17 "title": "$[*].title.rendered",18 "excerpt": "$[*].excerpt.rendered",19 "featuredImageUrl": "$[*]._embedded['wp:featuredmedia'][0].source_url",20 "content": "$[*].content.rendered",21 "slug": "$[*].slug"22 }23 }24 ]25}Pro tip: The _embedded path uses single quotes inside brackets: $[*]._embedded['wp:featuredmedia'][0].source_url — include the quotes exactly or the JSON path won't resolve.
Expected result: The API Calls panel shows a 'WordPress' group with a 'Get Posts' call. Testing it returns a list of posts with titles, excerpts, and featured image URLs populated correctly.
Render WordPress HTML content using a Custom Widget
WordPress stores post content and excerpts as HTML strings in the content.rendered and excerpt.rendered fields. A standard Flutter Text widget will display the raw HTML tags (like <p>, <strong>, <img>) as literal text rather than rendering them. To display formatted post bodies, you need an HTML rendering Custom Widget. In FlutterFlow, go to Custom Code in the left nav, click + Add, and choose Widget. Name it 'HtmlContentWidget'. In the Dependencies field, add flutter_html as a pub.dev package (current version — check pub.dev for the latest). Paste the Dart code below. This widget accepts a single String parameter called htmlContent and renders it with flutter_html's Html widget, which handles headings, paragraphs, bold, italic, links, and inline images. After creating the widget, go to your post detail screen. Add the HtmlContentWidget from the Widget Palette under Custom Widgets. In the Properties panel, bind the htmlContent parameter to your WPPost.content field from your Data Type or page state. Set a reasonable width (Match Parent) and let the height be dynamic. Important: Custom Widgets do not render in FlutterFlow's web Test Mode — you must use Run mode or test on a real device or emulator to see the HTML rendered correctly. For post cards showing just an excerpt, you can use a simpler approach: strip the HTML tags using a Dart Custom Action (replace all <[^>]+> with an empty string) and display the result in a standard Text widget.
1import 'package:flutter/material.dart';2import 'package:flutter_html/flutter_html.dart';34class HtmlContentWidget extends StatelessWidget {5 const HtmlContentWidget({6 super.key,7 required this.htmlContent,8 this.width,9 this.height,10 });1112 final String htmlContent;13 final double? width;14 final double? height;1516 @override17 Widget build(BuildContext context) {18 return SizedBox(19 width: width,20 height: height,21 child: SingleChildScrollView(22 child: Html(23 data: htmlContent,24 style: {25 'body': Style(26 fontSize: FontSize(16.0),27 fontFamily: 'Roboto',28 color: Theme.of(context).textTheme.bodyLarge?.color,29 ),30 'h1': Style(fontSize: FontSize(24.0)),31 'h2': Style(fontSize: FontSize(20.0)),32 'p': Style(margin: Margins.only(bottom: 12.0)),33 },34 ),35 ),36 );37 }38}Pro tip: For excerpt previews on cards, create a Custom Action that strips HTML tags (htmlContent.replaceAll(RegExp(r'<[^>]+>'), '')) — lighter than loading the full flutter_html widget in every list item.
Expected result: The HtmlContentWidget appears in your Custom Widgets list. When bound to a WordPress post's content.rendered field on device, it renders formatted text with proper paragraphs, headings, and inline images.
Proxy authenticated writes via a Firebase Cloud Function
If your app needs to create, update, or delete WordPress posts — not just read them — you'll use WordPress Application Passwords for authentication. Application Passwords are per-app tokens generated in WordPress Admin → Users → Your Profile → scroll to Application Passwords. Enter a name for the app, click Add New Application Password, and copy the generated password immediately (it's only shown once). The credential is used as HTTP Basic Auth: the header is Authorization: Basic base64(username:application_password). Since this credential must never be embedded in the Flutter app bundle, route authenticated requests through a Firebase Cloud Function. The function stores the WordPress username and Application Password as environment secrets (set via the Firebase Console → Functions → Configuration, or using firebase functions:secrets:set in the Firebase CLI). FlutterFlow calls your Cloud Function endpoint instead of WordPress directly — passing the post data as a JSON body. The function builds the WordPress API request with the Authorization header attached and forwards it. In FlutterFlow, add a new API Call in the WordPress group named 'Create Post (via proxy)'. Set the method to POST, and the endpoint to your Cloud Function URL (e.g., https://us-central1-your-project.cloudfunctions.net/wpProxy). Add a JSON body variable containing the post fields. This way, the Application Password never touches the client. If you'd rather skip building the proxy yourself, RapidDev's team builds FlutterFlow integrations like this every week — free scoping call at rapidevelopers.com/contact.
1// Firebase Cloud Function (Node.js) — index.js2const { onRequest } = require('firebase-functions/v2/https');3const { defineSecret } = require('firebase-functions/params');4const fetch = require('node-fetch');56const WP_USERNAME = defineSecret('WP_USERNAME');7const WP_APP_PASSWORD = defineSecret('WP_APP_PASSWORD');8const WP_BASE_URL = 'https://yoursite.com/wp-json/wp/v2';910exports.wpProxy = onRequest(11 { secrets: [WP_USERNAME, WP_APP_PASSWORD] },12 async (req, res) => {13 res.set('Access-Control-Allow-Origin', '*');14 if (req.method === 'OPTIONS') {15 res.set('Access-Control-Allow-Methods', 'POST');16 res.set('Access-Control-Allow-Headers', 'Content-Type');17 return res.status(204).send('');18 }1920 const credentials = Buffer.from(21 `${WP_USERNAME.value()}:${WP_APP_PASSWORD.value()}`22 ).toString('base64');2324 const { endpoint, method, body } = req.body;25 const wpResponse = await fetch(`${WP_BASE_URL}${endpoint}`, {26 method: method || 'POST',27 headers: {28 Authorization: `Basic ${credentials}`,29 'Content-Type': 'application/json',30 },31 body: JSON.stringify(body),32 });3334 const data = await wpResponse.json();35 return res.status(wpResponse.status).json(data);36 }37);Pro tip: Set your WordPress secrets using the Firebase CLI: firebase functions:secrets:set WP_USERNAME and firebase functions:secrets:set WP_APP_PASSWORD — never paste them in plaintext in the Firebase Console or your code.
Expected result: The Cloud Function is deployed and callable. When FlutterFlow sends a POST to the function URL with post data, the function creates a new draft post in WordPress and returns the created post's ID.
Add pagination and pull-to-refresh to the post list
WordPress REST API supports cursor-based pagination via the page and per_page query parameters. For a FlutterFlow ListView showing blog posts, you'll want to implement pagination so the app doesn't fetch all posts at once — especially for sites with hundreds of entries. In your Get Posts API Call, ensure the page variable is defined with a default value of 1 (Integer type). In your ListView screen, add a Page State variable called currentPage (Integer, initial value 1). Use an Action Flow on your ListView's scroll position or a 'Load More' button: when triggered, increment currentPage by 1, then call the Get Posts API with the updated page value, and append the results to your existing list using a Combine action. For pull-to-refresh, wrap your ListView in a RefreshIndicator widget (available in FlutterFlow's widget palette). On the onRefresh callback, reset currentPage to 1 and re-fetch the posts — this replaces the existing list data. Add the per_page variable to the API Call and set it to 10 or 20 items based on your design. WordPress also returns total post count and total pages in the response headers (X-WP-Total and X-WP-TotalPages), which you can use to show a 'no more posts' indicator when you've reached the end of the list. Check the Headers tab in your API Call Response to read these values.
Pro tip: WordPress returns X-WP-TotalPages in the response headers. Check this value against your currentPage to hide the 'Load More' button when all pages are loaded — preventing unnecessary API calls.
Expected result: The post list loads 10 items initially. Scrolling to the bottom and tapping 'Load More' fetches the next page and appends the posts. Pulling down from the top of the list refreshes and shows the latest posts.
Common use cases
Mobile news app pulling posts from a WordPress blog
A FlutterFlow app that displays a paginated feed of WordPress blog posts with featured images, categories, and excerpts. Tapping a card opens the full post rendered via an HTML widget. The app supports pull-to-refresh and category filtering via query params.
Build a news feed screen in FlutterFlow that fetches the latest 10 WordPress posts from my site, shows the featured image, title, and excerpt in a card list, and opens the full post body on tap.
Copy this prompt to try it in FlutterFlow
Content management companion app for WordPress authors
An internal FlutterFlow app that lets content authors create draft posts, update titles and body text, and change post status — all authenticated via Application Passwords proxied through Firebase. Read operations load the post list directly; write operations go through the secure proxy.
Build a FlutterFlow app that lets me log in and create or edit WordPress posts from my phone. Show a list of my recent posts and a form to create a new draft.
Copy this prompt to try it in FlutterFlow
Product catalog app powered by WordPress custom post types
A FlutterFlow catalog app that reads a custom post type (e.g., 'products') from a WordPress site using the REST API endpoint /wp-json/wp/v2/products. Each product card shows the featured image, title, and custom field values, pulled via ACF's REST API extension.
Create a FlutterFlow product catalog that fetches items from a WordPress custom post type called 'products', shows thumbnails and prices, and lets users filter by category.
Copy this prompt to try it in FlutterFlow
Troubleshooting
GET /wp-json/wp/v2/posts returns 401 Unauthorized or 404 Not Found for public posts
Cause: A WordPress security plugin (Wordfence, iThemes Security, All-In-One Security) has restricted REST API access to authenticated users only, or the site is on a staging environment with HTTP Basic Auth protecting the whole site.
Solution: In your security plugin settings, find the REST API section and disable 'Restrict REST API access to logged-in users.' For Wordfence, go to Wordfence → All Options → General Options. For staging HTTP Auth, add an Authorization header with the staging credentials to your FlutterFlow API Group's shared headers. Alternatively, confirm Pretty Permalinks are enabled in WordPress → Settings → Permalinks (any option other than 'Plain').
Post content displays raw HTML tags like <p>, <strong>, and <img src=...> as literal text in the app
Cause: WordPress content.rendered and excerpt.rendered fields contain HTML strings. A standard Flutter Text widget does not render HTML — it displays the markup literally.
Solution: Use the flutter_html Custom Widget (see Step 3). For excerpt snippets on post cards, use a Custom Action that strips HTML tags: htmlContent.replaceAll(RegExp(r'<[^>]+>'), '') before binding to a Text widget. The flutter_html widget must be tested on a real device or emulator — it does not render in FlutterFlow's web Test Mode.
1// Strip HTML tags in a Custom Action2String stripHtml(String htmlContent) {3 return htmlContent.replaceAll(RegExp(r'<[^>]+>'), '').trim();4}Featured image is missing or the _embedded path returns null in the JSON
Cause: The ?_embed=1 query parameter was not added to the API Call, or the post has no featured image set in WordPress, or the JSON path for the embedded media is wrong.
Solution: Confirm the Query Parameter _embed is set to '1' (string '1', not integer) in your API Call's Query Parameters tab. In the JSON path, use $[*]._embedded['wp:featuredmedia'][0].source_url — include the single quotes around wp:featuredmedia. In FlutterFlow, handle nullable image URLs by setting a fallback placeholder in the Image widget's Not Set state.
Authenticated API calls (create/update post) return 403 Forbidden or 401 Unauthorized
Cause: The Application Password was passed directly in the FlutterFlow API Call header (shipping it in the app bundle) but the encoding is wrong, or the WordPress user role does not have post-creation permissions.
Solution: Move the Application Password to a Firebase Cloud Function proxy (see Step 4). The function must base64-encode username:application_password and set the Authorization: Basic header. Confirm the WordPress user has the 'Editor' or 'Author' role with permission to publish posts. Application Passwords require WordPress 5.6+ and must be enabled — some hosts disable them via the disable_application_passwords filter.
Best practices
- Always test the /wp-json/wp/v2/ endpoint in a browser before configuring FlutterFlow — security plugins blocking the API are the #1 cause of failed integrations.
- Use ?_embed=1 on every post list request to include featured images and author data in one call — avoids N+1 requests that slow down the post feed.
- Never place Application Passwords or any WordPress authentication credential in a FlutterFlow API Call header — proxy all write operations through Firebase Cloud Functions or Supabase Edge Functions.
- Use the per_page parameter (default 10) and implement pagination rather than fetching all posts at once — WordPress sites often have thousands of posts.
- Create a FlutterFlow Data Type (e.g., WPPost) to hold the parsed post fields — this lets you bind data cleanly to ListViews and detail screens without repeatedly re-parsing JSON paths.
- Handle the case where a post has no featured image by setting a placeholder in your Image widget — not all WordPress posts have featured media set.
- Strip HTML tags from excerpt.rendered for list card previews using a Custom Action — reserve the full flutter_html widget for the detail screen to keep list scroll performance smooth.
- Check the X-WP-TotalPages response header to implement a proper end-of-list indicator and avoid making empty page requests.
Alternatives
Choose Canva's Connect API if your app needs to surface design assets, brand kits, or exported graphics rather than text content from a CMS.
Choose the YouTube Data API if your content strategy centers on video embeds and channel data rather than blog posts and pages.
Choose Vimeo if you need privacy-controlled video playback embedded in a FlutterFlow app, rather than text-based headless CMS content.
Frequently asked questions
Do I need a WordPress plugin to use the REST API in FlutterFlow?
No. The WordPress REST API has been built into WordPress core since version 4.7. You can start making GET requests to /wp-json/wp/v2/posts immediately with no additional plugins. The only plugins you might consider are ACF (Advanced Custom Fields) with its REST API support for custom field data, or WP REST Cache to improve performance for high-traffic apps.
Can I connect FlutterFlow to WordPress.com (not self-hosted)?
WordPress.com restricts REST API access differently from self-hosted WordPress. For WordPress.com sites, you'll use the WordPress.com REST API (v1.1 at public-api.wordpress.com/rest/v1.1/sites/{site}/posts) with a different base URL. Application Passwords work on WordPress.com Business and Commerce plans. The /wp-json/wp/v2/ endpoint described in this guide is specific to self-hosted WordPress installations.
Can FlutterFlow display the WordPress admin dashboard?
FlutterFlow is not designed to embed the WordPress admin interface. You're building a native Flutter app that communicates with WordPress data via the REST API. For content management from the app, you'd build custom screens in FlutterFlow that call the authenticated write endpoints (proxied through Cloud Functions), giving your team a tailored mobile editing experience rather than trying to wrap the web admin.
Will the flutter_html widget work on web builds of my FlutterFlow app?
The flutter_html package works on both mobile and web targets in Flutter. However, Custom Widgets do not render in FlutterFlow's in-browser Test Mode (the preview canvas) — they only render correctly in Run mode, on a real device, or in a production web build. Use Run mode or deploy a web build to verify HTML rendering before publishing.
How do I handle WordPress posts with custom post types in FlutterFlow?
Custom post types registered with the REST API (using show_in_rest: true in register_post_type) are accessible at /wp-json/wp/v2/{custom_post_type_slug}. For example, if you have a 'products' post type, you'd add a second API Call in your FlutterFlow WordPress group with endpoint /products. Parse the response into a separate Data Type and bind it to your product list screen.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation