Flutter Phone-to-Phone Power Share Tracker (Wireless Charging Hacker UI App)
Demo :
Click Video πππ
⭐ FEATURES :
-
Phone-to-phone wireless power tracker
-
BatteryManager API
-
NFC detection
-
Cyberpunk hacker UI
-
Live energy beam animation
-
All-device responsive
-
Viral Flutter project
Code :
import 'dart:async';
import 'dart:math' as math;
import 'dart:ui';
import 'package:battery_plus/battery_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:nfc_manager/nfc_manager.dart';
import 'package:flutter_animate/flutter_animate.dart';
void main() {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: Color(0xFF050505),
systemNavigationBarIconBrightness: Brightness.light,
));
runApp(const PowerShareApp());
}
class PowerShareApp extends StatelessWidget {
const PowerShareApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'PowerShare Protocol',
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: const Color(0xFF050505),
primaryColor: const Color(0xFF00F0FF),
colorScheme: const ColorScheme.dark(
primary: Color(0xFF00F0FF),
secondary: Color(0xFFD000FF),
surface: Color(0xFF101015),
),
textTheme: GoogleFonts.rajdhaniTextTheme(ThemeData.dark().textTheme),
),
home: const PowerShareHome(),
);
}
}
class PowerShareHome extends StatefulWidget {
const PowerShareHome({super.key});
@override
State<PowerShareHome> createState() => _PowerShareHomeState();
}
class _PowerShareHomeState extends State<PowerShareHome>
with TickerProviderStateMixin {
// Battery State
final Battery _battery = Battery();
int _batteryLevel = 100;
BatteryState _batteryState = BatteryState.full;
StreamSubscription<BatteryState>? _batteryStateSubscription;
// NFC & Connection State
bool _isNfcAvailable = false;
bool _isScanning = false;
bool _isConnected = false;
String _connectionStatus = "STANDBY";
String _connectedDeviceName = "";
// Power Sharing State
bool _isSharing = false;
bool _isReceiver = false; // Toggle between Giver/Receiver for simulation
double _transferEfficiency = 0.0;
double _transferRate = 0.0; // Watts
Timer? _simulationTimer;
// Animations
late AnimationController _pulseController;
late AnimationController _beamController;
@override
void initState() {
super.initState();
_initBattery();
_initNfc();
_pulseController = AnimationController(
vsync: this, duration: const Duration(seconds: 2))..repeat();
_beamController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 1500))..repeat();
}
@override
void dispose() {
_batteryStateSubscription?.cancel();
_simulationTimer?.cancel();
_pulseController.dispose();
_beamController.dispose();
super.dispose();
}
Future<void> _initBattery() async {
try {
final level = await _battery.batteryLevel;
_batteryStateSubscription =
_battery.onBatteryStateChanged.listen((BatteryState state) {
setState(() {
_batteryState = state;
});
});
if (mounted) {
setState(() {
_batteryLevel = level;
});
}
} catch (e) {
debugPrint("Battery Error: $e");
}
}
Future<void> _initNfc() async {
bool isAvailable = await NfcManager.instance.isAvailable();
setState(() {
_isNfcAvailable = isAvailable;
});
if (isAvailable) {
// Start listening for tags if relevant, or wait for manual trigger
// strict "Phone-to-Phone" usually requires Android Beam (deprecated) or proprietary.
// We will simulate the connection trigger via NFC tag discovery if present.
}
}
void _toggleScan() {
if (_isConnected) {
_disconnect();
return;
}
setState(() {
_isScanning = !_isScanning;
_connectionStatus = _isScanning ? "SEARCHING..." : "STANDBY";
});
if (_isScanning) {
// If NFC is available, try to read. If not, just simulate finding after delay.
if (_isNfcAvailable) {
NfcManager.instance.startSession(
pollingOptions: {NfcPollingOption.iso14443, NfcPollingOption.iso15693},
onDiscovered: (NfcTag tag) async {
// Simulate finding a compatible phone
_handleDeviceFound("NFC_DEVICE_${tag.data.hashCode.toString().substring(0, 4)}");
NfcManager.instance.stopSession();
}).catchError((e) {
// Fallback to simulation if error
});
}
// Simulation Fallback for Demo
Future.delayed(const Duration(seconds: 3), () {
if (mounted && _isScanning && !_isConnected) {
_handleDeviceFound("GALAXY_S29_ULTRA");
}
});
} else {
NfcManager.instance.stopSession().catchError((_) {});
}
}
void _handleDeviceFound(String deviceName) {
setState(() {
_isScanning = false;
_isConnected = true;
_connectedDeviceName = deviceName;
_connectionStatus = "LINK ESTABLISHED";
_isSharing = true;
_startSharingSimulation();
});
}
void _disconnect() {
_simulationTimer?.cancel();
setState(() {
_isConnected = false;
_isSharing = false;
_connectionStatus = "STANDBY";
_transferEfficiency = 0;
_transferRate = 0;
});
}
void _startSharingSimulation() {
_simulationTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
if (!mounted) return;
setState(() {
// Fluctuate values for realism
_transferEfficiency = 85.0 + math.Random().nextDouble() * 14.0;
_transferRate = (_isReceiver ? 15.0 : -15.0) + math.Random().nextDouble() * 2.0;
// Simulate battery change slowly
if (timer.tick % 20 == 0) {
// every 10 seconds
if (_isReceiver) {
_batteryLevel = (_batteryLevel + 1).clamp(0, 100);
} else {
_batteryLevel = (_batteryLevel - 1).clamp(0, 100);
}
}
});
});
}
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final isMobile = size.width < 600;
return Scaffold(
body: Stack(
children: [
// 1. Background Grid & Particles
Positioned.fill(
child: CustomPaint(
painter: GridPainter(
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
scroll: _pulseController.value,
),
),
),
// 2. Animated Background Glow
Positioned(
top: -100,
left: -100,
child: Container(
width: 300,
height: 300,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.secondary.withOpacity(0.15),
boxShadow: [BoxShadow(color: Theme.of(context).colorScheme.secondary, blurRadius: 100)],
),
).animate(onPlay: (c) => c.repeat(reverse: true)).scale(duration: 4.seconds, begin: const Offset(1,1), end: const Offset(1.5,1.5)),
),
// 3. Main Content
SafeArea(
child: Column(
children: [
_buildHeader(),
Expanded(
child: LayoutBuilder(
builder: (context, constraints) {
if (!isMobile) {
return Row(
children: [
Expanded(child: _buildPhoneVisual(isMe: true)),
Expanded(child: _buildConnectionVisual()),
Expanded(child: _buildPhoneVisual(isMe: false)),
],
);
} else {
// Mobile Layout: Vertical
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildPhoneVisual(isMe: true),
Expanded(child: _buildConnectionVisual()),
if (_isConnected) _buildPhoneVisual(isMe: false),
],
);
}
},
),
),
_buildHUD(),
const SizedBox(height: 20),
_buildControls(),
const SizedBox(height: 20),
],
),
),
],
),
);
}
Widget _buildHeader() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(Icons.bolt, color: Theme.of(context).primaryColor),
const SizedBox(width: 8),
Text(
"POWER_SHARE // PROTOCOL",
style: GoogleFonts.orbitron(
fontSize: 16,
fontWeight: FontWeight.bold,
letterSpacing: 2,
color: Colors.white,
),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
border: Border.all(color: _isNfcAvailable ? Colors.green : Colors.red),
borderRadius: BorderRadius.circular(4),
color: (_isNfcAvailable ? Colors.green : Colors.red).withOpacity(0.1),
),
child: Text(
_isNfcAvailable ? "NFC ACTIVE" : "NFC OFFLINE",
style: TextStyle(
fontSize: 10,
color: _isNfcAvailable ? Colors.green : Colors.red,
fontWeight: FontWeight.bold,
),
),
)
],
),
);
}
Widget _buildPhoneVisual({required bool isMe}) {
// If not connected and not me, show nothing or empty slot
if (!isMe && !_isConnected) return const SizedBox.shrink();
final color = isMe
? (_isReceiver ? Colors.redAccent : Colors.cyanAccent)
: (_isReceiver ? Colors.cyanAccent : Colors.redAccent); // Opposite
// Logic: If I am receiver, I am Red (Low/Charging) -> He is Cyan (Source).
// Wait, usually Source=Green/Cyan, Receiver=Red/Yellow.
// If I am Receiver: My Battery is getting charge.
final level = isMe ? _batteryLevel : (100 - _batteryLevel); // Fake other battery
final label = isMe ? "LOCAL_DEVICE" : "REMOTE_LINK";
final subLabel = isMe ? "THIS DEVICE" : _connectedDeviceName;
return Container(
margin: const EdgeInsets.all(20),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: const Color(0xFF1A1A20),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: color.withOpacity(0.5), width: 1),
boxShadow: [
BoxShadow(color: color.withOpacity(0.1), blurRadius: 20, spreadRadius: 5),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(isMe ? Icons.smartphone : Icons.phonelink_ring, size: 40, color: color),
const SizedBox(height: 10),
Text(label, style: GoogleFonts.rajdhani(color: Colors.white54, fontSize: 12, letterSpacing: 1.5)),
Text(subLabel, style: GoogleFonts.orbitron(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)),
const SizedBox(height: 20),
Stack(
alignment: Alignment.center,
children: [
CircularProgressIndicator(
value: level / 100,
strokeWidth: 8,
backgroundColor: Colors.white10,
valueColor: AlwaysStoppedAnimation(color),
),
Text("$level%", style: GoogleFonts.orbitron(fontWeight: FontWeight.bold)),
],
),
const SizedBox(height: 10),
Text(isMe
? (_isReceiver ? "RECEIVING" : "TRANSMITTING")
: (_isReceiver ? "TRANSMITTING" : "RECEIVING"),
style: TextStyle(color: color, fontSize: 10, letterSpacing: 2)
),
],
),
).animate().fadeIn().slideY(begin: 0.1, end: 0);
}
Widget _buildConnectionVisual() {
if (!_isConnected) {
if (_isScanning) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(color: Colors.cyanAccent),
const SizedBox(height: 20),
Text("SCANNING NEAR FIELD...", style: GoogleFonts.orbitron(letterSpacing: 2, color: Colors.cyanAccent)),
],
),
);
}
return Center(
child: Text("LINK REQUIRED", style: GoogleFonts.rajdhani(color: Colors.white24, fontSize: 20)),
);
}
return AnimatedBuilder(
animation: _beamController,
builder: (context, child) {
return CustomPaint(
size: const Size(double.infinity, 200),
painter: EnergyBeamPainter(
animationValue: _beamController.value,
color: const Color(0xFF00F0FF),
isVertical: MediaQuery.of(context).size.width < 600,
),
);
},
);
}
Widget _buildHUD() {
if (!_isConnected && !_isScanning) return const SizedBox.shrink();
return GlassContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildHUDItem("STATUS", _connectionStatus, _isConnected ? Colors.greenAccent : Colors.amber),
if (_isConnected) ...[
_buildVerticalDivider(),
_buildHUDItem("EFFICIENCY", "${_transferEfficiency.toStringAsFixed(1)}%", Colors.cyanAccent),
_buildVerticalDivider(),
_buildHUDItem("RATE", "${_transferRate.abs().toStringAsFixed(1)}W", Colors.purpleAccent),
]
],
),
);
}
Widget _buildVerticalDivider() => Container(width: 1, height: 30, color: Colors.white24);
Widget _buildHUDItem(String label, String value, Color color) {
return Column(
children: [
Text(label, style: GoogleFonts.rajdhani(color: Colors.white54, fontSize: 10, fontWeight: FontWeight.bold)),
const SizedBox(height: 4),
Text(value, style: GoogleFonts.orbitron(color: color, fontSize: 18, fontWeight: FontWeight.bold)),
],
);
}
Widget _buildControls() {
return Column(
children: [
if (_isConnected)
Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("ROLE: ", style: GoogleFonts.rajdhani(color: Colors.white)),
Switch(
value: _isReceiver,
activeColor: Colors.redAccent,
inactiveThumbColor: Colors.cyanAccent,
onChanged: (val) {
setState(() {
_isReceiver = val;
});
}
),
Text(_isReceiver ? "RECEIVER" : "GIVER", style: TextStyle(
color: _isReceiver ? Colors.redAccent : Colors.cyanAccent,
fontWeight: FontWeight.bold
)),
],
),
),
GestureDetector(
onTap: _toggleScan,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 15),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: _isScanning
? [Colors.red.shade900, Colors.red.shade600]
: (_isConnected ? [Colors.red.shade900, Colors.red.shade600] : [Colors.cyan.shade900, Colors.cyan.shade600]),
),
boxShadow: [
BoxShadow(
color: (_isScanning || _isConnected) ? Colors.redAccent : Colors.cyanAccent,
blurRadius: 15,
offset: const Offset(0, 5),
)
],
borderRadius: BorderRadius.circular(30),
),
child: Text(
_isConnected ? "TERMINATE LINK" : (_isScanning ? "CANCEL SCAN" : "INITIATE SCAN"),
style: GoogleFonts.orbitron(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
letterSpacing: 2,
),
),
),
),
],
);
}
}
// ---------------------------
// PAINTERS for Sci-Fi Effects
// ---------------------------
class GridPainter extends CustomPainter {
final Color color;
final double scroll;
GridPainter({required this.color, required this.scroll});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..strokeWidth = 1
..style = PaintingStyle.stroke;
const spacing = 40.0;
// Draw Grid with perspective illusion if possible, plain for now
for (double i = 0; i < size.width; i += spacing) {
canvas.drawLine(Offset(i, 0), Offset(i, size.height), paint);
}
for (double i = 0; i < size.height; i += spacing) {
double y = (i + (scroll * spacing)) % size.height;
canvas.drawLine(Offset(0, y), Offset(size.width, y), paint);
}
}
@override
bool shouldRepaint(covariant GridPainter oldDelegate) => true;
}
class EnergyBeamPainter extends CustomPainter {
final double animationValue;
final Color color;
final bool isVertical;
EnergyBeamPainter({required this.animationValue, required this.color, required this.isVertical});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.stroke
..strokeWidth = 3
..shader = LinearGradient(
colors: [color.withOpacity(0), color, color.withOpacity(0)],
stops: const [0.0, 0.5, 1.0],
).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
final path = Path();
if (isVertical) {
// Wave flowing down
for (double i = 0; i < size.height; i++) {
double x = size.width / 2 +
math.sin((i / 20) + (animationValue * 2 * math.pi)) * 10 +
math.cos((i / 30) + (animationValue * 4 * math.pi)) * 5;
if (i == 0) path.moveTo(x, i);
else path.lineTo(x, i);
}
} else {
// Wave flowing right
for (double i = 0; i < size.width; i++) {
double y = size.height / 2 +
math.sin((i / 20) + (animationValue * 2 * math.pi)) * 10 +
math.cos((i / 30) + (animationValue * 4 * math.pi)) * 5;
if (i == 0) path.moveTo(i, y);
else path.lineTo(i, y);
}
}
canvas.drawPath(path, paint);
// Core beam
final corePaint = Paint()..color = Colors.white.withOpacity(0.5)..strokeWidth = 1;
if (isVertical) {
canvas.drawLine(Offset(size.width/2, 0), Offset(size.width/2, size.height), corePaint);
} else {
canvas.drawLine(Offset(0, size.height/2), Offset(size.width, size.height/2), corePaint);
}
}
@override
bool shouldRepaint(EnergyBeamPainter oldDelegate) => true;
}
// ---------------------------
// UTILITY WIDGETS
// ---------------------------
class GlassContainer extends StatelessWidget {
final Widget child;
const GlassContainer({super.key, required this.child});
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(15),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(15),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: child,
),
),
);
}
}
Comments
Post a Comment