Flutter
8 min read
•
Updated Aug 2026
How to Build a 4K Wallpaper App in Flutter Using NexWall Free REST API (Full 2026 Tutorial)
Step-by-step complete guide with source code to create an Android & iOS 4K wallpaper app using Flutter, Provider/Bloc, cached_network_image, and NexWall JSON API.
1. Introduction & Prerequisites
In this guide, we will build a production-grade 4K Wallpaper app in Flutter that fetches categorized wallpapers from the NexWall Free REST API. We will use the http package for network requests and cached_network_image for ultra-smooth edge caching.
2. Adding Dependencies
Add the following packages to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
http: ^1.2.0
cached_network_image: ^3.3.1
flutter_staggered_grid_view: ^0.7.0
3. Building the Wallpaper Model
Create a file named models/wallpaper_model.dart:
class WallpaperItem {
final int id;
final int categoryId;
final String imageUrl;
final String thumbnailUrl;
final bool isPremium;
final String resolution;
WallpaperItem({
required this.id,
required this.categoryId,
required this.imageUrl,
required this.thumbnailUrl,
required this.isPremium,
required this.resolution,
});
factory WallpaperItem.fromJson(Map<String, dynamic> json) {
return WallpaperItem(
id: json['id'] ?? 0,
categoryId: json['category_id'] ?? 0,
imageUrl: json['image_url'] ?? '',
thumbnailUrl: json['thumbnail_url'] ?? (json['image_url'] ?? ''),
isPremium: json['is_premium'] ?? false,
resolution: json['resolution'] ?? '2160x3840',
);
}
}
4. API Service Implementation
Create services/wallpaper_api_service.dart and paste your NexWall Bearer API token:
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/wallpaper_model.dart';
class WallpaperApiService {
static const String _baseUrl = 'https://nexwall.kodnextech.com/api/developer/v1';
static const String _apiKey = 'YOUR_NEXWALL_BEARER_API_KEY';
static Future<List<WallpaperItem>> fetchWallpapers({int page = 1, int? categoryId}) async {
final queryParams = {
'page': page.toString(),
'per_page': '24',
if (categoryId != null) 'category_id': categoryId.toString(),
};
final uri = Uri.parse('$_baseUrl/wallpapers').replace(queryParameters: queryParams);
final response = await http.get(
uri,
headers: {
'Authorization': 'Bearer $_apiKey',
'Accept': 'application/json',
},
);
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body);
final List data = decoded['data'] ?? [];
return data.map((item) => WallpaperItem.fromJson(item)).toList();
} else {
throw Exception('Failed to load wallpapers: ${response.statusCode}');
}
}
}
5. Grid View UI Implementation
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'services/wallpaper_api_service.dart';
import 'models/wallpaper_model.dart';
class WallpaperFeedScreen extends StatefulWidget {
@override
_WallpaperFeedScreenState createState() => _WallpaperFeedScreenState();
}
class _WallpaperFeedScreenState extends State<WallpaperFeedScreen> {
late Future<List<WallpaperItem>> _wallpapersFuture;
@override
void initState() {
super.initState();
_wallpapersFuture = WallpaperApiService.fetchWallpapers();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF0F1117),
appBar: AppBar(
title: const Text('NexWall 4K Wallpapers'),
backgroundColor: Colors.transparent,
elevation: 0,
),
body: FutureBuilder<List<WallpaperItem>>(
future: _wallpapersFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator(color: Color(0xFF7C8CFF)));
}
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}', style: const TextStyle(color: Colors.white70)));
}
final wallpapers = snapshot.data ?? [];
return GridView.builder(
padding: const EdgeInsets.all(12),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 9 / 16,
),
itemCount: wallpapers.length,
itemBuilder: (context, index) {
final item = wallpapers[index];
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: CachedNetworkImage(
imageUrl: item.thumbnailUrl,
fit: BoxFit.cover,
placeholder: (context, url) => Container(color: const Color(0xFF1E2130)),
errorWidget: (context, url, error) => const Icon(Icons.broken_image, color: Colors.grey),
),
);
},
);
},
),
);
}
}
Want to test this endpoint right now?
Use the interactive Live Sandbox Console to test queries without writing code.