When designing interfaces for AI platforms, fintech dashboards, or futuristic applications, traditional UI patterns often feel outdated. I wanted to experiment with something deeper — something that felt alive.
So I built a 3D rotating neural sphere with a galaxy background and a glassmorphism login interface using pure Flutter.
No third-party 3D engine.
No external animation packages.
Just clean Flutter + CustomPainter.
Animated Galaxy Background
To create depth, I added a moving starfield in the background.
Using CustomPainter, small stars are drawn on the canvas and animated vertically. The slow movement creates a calm, space-like atmosphere without distracting the user.
This keeps performance smooth while making the UI feel dynamic.
3D Rotating Particle Sphere
The main highlight is a rotating sphere made of tiny particles.
Each particle is placed using mathematical angles and converted into 2D positions. By adjusting opacity and size based on depth, the sphere looks 3D — even though Flutter is a 2D framework.
The sphere:
Can also be rotated manually using gestures
Rotates automatically
This adds interaction and makes the interface feel premium.
Glassmorphism Login Panel
On top of everything, I placed a blurred glass-style login card.
Using:
- Backdrop blur
- Soft white borders
- Minimal typography
The form feels like a floating “neural access terminal.”
Full Source Code:
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(
const MaterialApp(debugShowCheckedModeBanner: false, home: LucrativeSphere()),
);
class LucrativeSphere extends StatefulWidget {
const LucrativeSphere({super.key});
@override
State<LucrativeSphere> createState() => _LucrativeSphereState();
}
class _LucrativeSphereState extends State<LucrativeSphere>
with TickerProviderStateMixin {
late AnimationController _autoRotationController;
final List<Particle> _particles = [];
final List<GalaxyStar> _galaxyStars = [];
double _manualRotationX = 0;
double _manualRotationY = 0;
@override
void initState() {
super.initState();
final random = math.Random();
for (int i = 0; i < 1000; i++) {
_particles.add(
Particle(
theta: random.nextDouble() * 2 * math.pi,
phi: random.nextDouble() * math.pi,
size: random.nextDouble() * 1.5 + 0.2,
),
);
}
for (int i = 0; i < 200; i++) {
_galaxyStars.add(
GalaxyStar(
x: random.nextDouble(),
y: random.nextDouble(),
size: random.nextDouble() * 3,
velocity: random.nextDouble() * 0.02 + 0.005,
),
);
}
_autoRotationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 50),
)..repeat();
}
@override
void dispose() {
_autoRotationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF010105),
body: GestureDetector(
onPanUpdate: (details) {
setState(() {
_manualRotationY += details.delta.dx * 0.008;
_manualRotationX -= details.delta.dy * 0.008;
});
},
child: Stack(
alignment: Alignment.center,
children: [
// 1. Background Galaxy
AnimatedBuilder(
animation: _autoRotationController,
builder:
(context, child) => CustomPaint(
size: MediaQuery.of(context).size,
painter: GalaxyPainter(
_galaxyStars,
_autoRotationController.value,
),
),
),
// 2. The 3D Sphere
Positioned(
top: 40,
child: AnimatedBuilder(
animation: _autoRotationController,
builder:
(context, child) => CustomPaint(
size: MediaQuery.of(context).size / 1.6,
painter: SpherePainter(
particles: _particles,
rotationY:
(_autoRotationController.value * 2 * math.pi) +
_manualRotationY,
rotationX: _manualRotationX,
),
),
),
),
// 3. The Lucrative Glass Form
Positioned(bottom: 200, child: _buildGlassForm()),
],
),
),
);
}
Widget _buildGlassForm() {
return ClipRRect(
borderRadius: BorderRadius.circular(30),
child: BackdropFilter(
// This hides/diffuses the particles behind the form
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: Container(
width: 300,
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 50),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.03),
borderRadius: BorderRadius.circular(30),
border: Border.all(color: Colors.white.withOpacity(0.12), width: 1),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"NEURAL ACCESS",
style: TextStyle(
color: Colors.white,
letterSpacing: 8,
fontSize: 14,
fontWeight: FontWeight.w200,
),
),
const SizedBox(height: 40),
_buildField("IDENTITY_TOKEN"),
const SizedBox(height: 20),
_buildField("SECURITY_KEY", isPassword: true),
const SizedBox(height: 40),
_buildButton(),
],
),
),
),
);
}
Widget _buildField(String hint, {bool isPassword = false}) {
return TextField(
obscureText: isPassword,
style: const TextStyle(color: Colors.white, fontSize: 13),
decoration: InputDecoration(
hintText: hint,
hintStyle: TextStyle(
color: Colors.white.withOpacity(0.2),
letterSpacing: 2,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white.withOpacity(0.1)),
),
focusedBorder: const UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blueAccent),
),
),
);
}
Widget _buildButton() {
return Container(
width: double.infinity,
height: 45,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(4),
boxShadow: [
BoxShadow(color: Colors.white.withOpacity(0.1), blurRadius: 20),
],
),
child: TextButton(
onPressed: () {},
child: const Text(
"INITIALIZE",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w900,
letterSpacing: 2,
fontSize: 12,
),
),
),
);
}
}
// --- Logic Classes ---
class Particle {
final double theta, phi, size;
Particle({required this.theta, required this.phi, required this.size});
}
class GalaxyStar {
final double x, y, size, velocity;
GalaxyStar({
required this.x,
required this.y,
required this.size,
required this.velocity,
});
}
class SpherePainter extends CustomPainter {
final List<Particle> particles;
final double rotationY, rotationX;
SpherePainter({
required this.particles,
required this.rotationY,
required this.rotationX,
});
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width * 0.45;
final paint = Paint();
for (var p in particles) {
double rT = p.theta + rotationY;
double rP = p.phi + rotationX;
double x = radius * math.sin(rP) * math.cos(rT);
double y = radius * math.cos(rP);
double z = radius * math.sin(rP) * math.sin(rT);
double zDepth = (z + radius) / (2 * radius);
paint.color = Colors.white.withOpacity(0.1 + (zDepth * 0.7));
canvas.drawCircle(
Offset(center.dx + x, center.dy + y),
p.size * (0.5 + zDepth),
paint,
);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
class GalaxyPainter extends CustomPainter {
final List<GalaxyStar> stars;
final double progress;
GalaxyPainter(this.stars, this.progress);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.white.withOpacity(0.2);
for (var s in stars) {
double movingY = (s.y + (progress * s.velocity * 5)) % 1.0;
canvas.drawCircle(
Offset(s.x * size.width, movingY * size.height),
s.size,
paint,
);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
Flutter is more powerful than we think.
With just math and CustomPainter, we can build immersive 3D-like interfaces — without any external engine.
Sometimes, great UI is not about more packages.
It’s about creativity.
