惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Know Your Adversary
Know Your Adversary
T
The Blog of Author Tim Ferriss
G
Google Developers Blog
F
Fortinet All Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Engineering at Meta
Engineering at Meta
Blog — PlanetScale
Blog — PlanetScale
M
MIT News - Artificial intelligence
H
Help Net Security
Recent Announcements
Recent Announcements
SecWiki News
SecWiki News
F
Full Disclosure
GbyAI
GbyAI
Attack and Defense Labs
Attack and Defense Labs
PCI Perspectives
PCI Perspectives
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
L
LangChain Blog
T
Troy Hunt's Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Y
Y Combinator Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Recorded Future
Recorded Future
N
News and Events Feed by Topic
IT之家
IT之家
Hacker News - Newest:
Hacker News - Newest: "LLM"
I
InfoQ
小众软件
小众软件
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
H
Hackread – Cybersecurity News, Data Breaches, AI and More
P
Proofpoint News Feed
有赞技术团队
有赞技术团队
T
Tailwind CSS Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hugging Face - Blog
Hugging Face - Blog
B
Blog
人人都是产品经理
人人都是产品经理
V
V2EX
www.infosecurity-magazine.com
www.infosecurity-magazine.com
B
Blog RSS Feed
S
Security Affairs
The GitHub Blog
The GitHub Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Security Archives - TechRepublic
Security Archives - TechRepublic
Webroot Blog
Webroot Blog
D
Docker
TaoSecurity Blog
TaoSecurity Blog
Google Online Security Blog
Google Online Security Blog
C
Check Point Blog
A
Arctic Wolf

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Common Null Safety Mistakes
FARINU TAIWO · 2026-05-20 · via DEV Community

Dart’s null safety gives you a strong contract, but there are still ways to weaken it in practice.

Flutter’s sound null safety is one of Dart’s most valuable features. When the compiler warns that a value might be null, it is not being overly strict. It is pointing out a real edge case that can surface in production if ignored.

However, null safety does not enforce your business logic. You can satisfy the type system using ?? '', !, or chained ?. operators while still introducing subtle bugs that are often harder to trace than a direct crash like Null check operator used on a null value.

These patterns around nullable fields can quietly build up as technical debt. They may feel convenient or safe at the moment, but they often create hidden problems that show up later in production.


1. Using ?? '' as a silent pass through to repository calls

This is one of the most common patterns and also one of the riskiest.

// ❌ The compiler is satisfied. The API is not.
final result = await profileRepo.updatePhone(phone: user.phone ?? '');

Enter fullscreen mode Exit fullscreen mode

At first glance, this seems safe because the null case is handled. In reality, you have replaced a type safety issue with a semantic one. Most backends do not treat an empty string the same as a missing value.

This can lead to:

  • Overwriting an existing phone number with an empty string
  • Triggering backend validation errors only at runtime
  • Passing validation silently while corrupting stored data

The fallback ?? '' creates a false sense of safety. It tells the compiler everything is fine, but it hides the fact that important information is missing. The null value was meaningful because it indicated the user has not provided a phone number, and that meaning is lost.

A clearer and safer approach is to explicitly handle the null case

// ✅ Explicit handling makes intent clear
final phone = user.phone;

if (phone == null) {
  // Show validation error, log, or stop execution
  return;
}

final result = await profileRepo.updatePhone(phone: phone);

Enter fullscreen mode Exit fullscreen mode

Now Dart promotes phone to a non nullable String, and your intent is explicit. There is no ambiguity about what should happen when the value is missing.

The same issue appears with required IDs

// ❌ Hides missing required data
await orderRepo.fetchDetails(orderId: order.id ?? '');

Enter fullscreen mode Exit fullscreen mode

// ✅ Explicit handling of missing value
final orderId = order.id;

if (orderId == null) {
  // Handle error state appropriately
  return;
}

await orderRepo.fetchDetails(orderId: orderId);

Enter fullscreen mode Exit fullscreen mode

2. Using ! on Text Widget Values

The ! operator is a runtime assertion that says “I am certain this value is not null.” In a Text widget, that certainty is only checked at render time. That can happen after async updates, after a rebuild, after a logout that clears the user model, or while data is still loading.

// ❌ Crashes with "Null check operator used on a null value"
// when the user model is cleared or before profile data loads
Text(viewModel.user!.name!)

Enter fullscreen mode Exit fullscreen mode

When this fails, the error happens inside the build method. Flutter cannot recover cleanly from that, so you get a red screen in development and potentially a broken or blank UI in production.

A safer approach is to provide a meaningful fallback

// ✅ Safe fallback for UI state
Text(viewModel.user?.name ?? 'Guest')

Enter fullscreen mode Exit fullscreen mode

The fallback value like 'Guest' is appropriate here because UI can safely display a default state when data is missing. This is different from backend or repository calls where missing data usually has business meaning.

The key distinction is this: using ?? '' in API calls can silently corrupt data, while using ?? 'Guest' in UI is a deliberate presentation choice.

The same rule applies to widgets that require non-null values

// ❌ Unsafe forced unwrapping
Image.network(product.imageUrl!)

Enter fullscreen mode Exit fullscreen mode

if (product.imageUrl != null) {
  Image.network(product.imageUrl!)
} else {
  const PlaceholderImage()
}

Enter fullscreen mode Exit fullscreen mode

3. Passing null optional fields in query parameters and request payload

When building a Map<String, dynamic> for a GET request with filters or a PATCH request, you need to be intentional about which keys are included. Many REST APIs treat a key with a null value differently from a missing key entirely.

A null value can mean “clear this field” in some backends, or it can trigger validation errors. A missing key usually means “do not change this field.”

❌ Unsafe approach: sending null values

// ❌ Sends {"status": null, "category": null, "page": 1}
// Backend may interpret null as "clear this field"
final params = {
  'status': filter.status,
  'category': filter.category,
  'page': page,
};

The same issue can happen when using `json_serializable` if null inclusion is not configured properly.

Enter fullscreen mode Exit fullscreen mode

// ❌ includeIfNull: true will include null fields in the request body

Enter fullscreen mode Exit fullscreen mode

Safe approach: include only non-null values

// ✅ Only include values that exist
final params = <String, dynamic>{
  'page': page,
};

if (filter.status != null) {
  params['status'] = filter.status;
}

if (filter.category != null) {
  params['category'] = filter.category;
}

Enter fullscreen mode Exit fullscreen mode

Cleaner approach: remove nulls from a map

extension MapNullStrip on Map<String, dynamic> {
  Map<String, dynamic> withoutNulls() =>
      Map.fromEntries(entries.where((e) => e.value != null));
}

final params = {
  'status': filter.status,
  'category': filter.category,
  'page': page,
}.withoutNulls();

Enter fullscreen mode Exit fullscreen mode

Using Dio with query parameters

In Dio, query parameters are passed using queryParameters. The same rule applies: avoid sending null values.

final response = await dio.get(
  '/orders',
  queryParameters: {
    'status': filter.status,
    'category': filter.category,
    'page': page,
  }.withoutNulls(),
);

Enter fullscreen mode Exit fullscreen mode

This ensures only meaningful filters are sent to the backend.

Using Dio with request body (POST or PATCH)

For request payload, the same principle applies.

final response = await dio.patch(
  '/profile',
  data: {
    'phone': phone,
    'bio': bio,
    'avatar': avatarUrl,
  }.withoutNulls(),
);

Enter fullscreen mode Exit fullscreen mode

Using json_serializable for PATCH requests

For PATCH semantics, you usually want only changed fields to be sent. You can enforce this at the model level.

@JsonSerializable(includeIfNull: false)
class UpdateProfileRequest {
  final String? phone;
  final String? bio;

  UpdateProfileRequest({
    this.phone,
    this.bio,
  });
}

Enter fullscreen mode Exit fullscreen mode

This ensures only non-null fields are serialized.

4. Null checking after navigation, not before

In Flutter, navigation passes data through route arguments or constructor parameters. When something is null, the crash often happens in the destination screen, not where the mistake was made. This makes debugging harder because the source of the problem is separated from the failure point.

❌ Unsafe approach: forcing null with !

// ❌ Crashes inside RideDetailsScreen if order.id is null
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (_) => RideDetailsScreen(orderId: order.id!),
  ),
);

Enter fullscreen mode Exit fullscreen mode

This pushes the responsibility of handling null into the widget tree. If it fails, the error shows up far away from where the bug was introduced.

✅ Safer approach: validate before navigation

final orderId = order.id;

if (orderId == null) {
  // Show error, log issue, or stop navigation
  return;
}

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (_) => RideDetailsScreen(orderId: orderId),
  ),
);

Enter fullscreen mode Exit fullscreen mode

This ensures that RideDetailsScreen always receives a valid non nullable value. The screen stays simple and does not need defensive null checks inside its constructor or UI.

The key idea is that the decision about whether navigation is allowed should happen before navigation, not after.

The same pattern with go_router

// ❌ Can lead to invalid or unintended route values
context.push('/order/${order.id}');

Enter fullscreen mode Exit fullscreen mode

✅ Safer approach: validate before navigation

final id = order.id;

if (id == null) return;

context.push('/order/$id');

Enter fullscreen mode Exit fullscreen mode

5. Silent ?? 0 on numeric business fields

Using ?? 0 as a fallback for null numeric values is often misleading in real business logic. While it prevents crashes, it can silently change the meaning of your data.

❌ Unsafe examples: hiding missing data as zero

// ❌ Shows ₦0.00 when price is null, which looks like a free product
Text('₦${product.price ?? 0}')

// ❌ Treats missing values as zero and silently changes the total
final total = cartItems.fold(
  0.0,
  (sum, item) => sum + (item.price ?? 0) * (item.quantity ?? 0),
);

// ❌ Replaces missing ID with a fake value, which can create invalid records
await api.assignRider(userId: rider.id ?? -1);

Enter fullscreen mode Exit fullscreen mode

The core problem is that null is meaningful. It usually represents missing, unknown, or not yet loaded data. Replacing it with zero removes that meaning.

Ask the right question: what does null actually mean here

In most applications, null typically means one of the following:

  • The data is still loading, so the UI should show a loading or placeholder state
  • The field is optional, so absence should be handled explicitly
  • Something went wrong, so an error state should be shown instead of a default value

✅ Safer approach: handle missing values explicitly

// ✅ Clear UI state for missing price
if (product.price == null) {
  const Text('Price unavailable');
} else {
  Text('₦${product.price!.toStringAsFixed(2)}');
}

// ✅ Validate before doing calculations
final price = item.price;
final quantity = item.quantity;

if (price == null || quantity == null) {
  // Skip item, log issue, or handle error state
  return;
}

final lineTotal = price * quantity;

Enter fullscreen mode Exit fullscreen mode

Using ?? 0 is only safe when zero is a truly meaningful default value, such as a display count.

For example:

Text('${product.reviewCount ?? 0} reviews')

Enter fullscreen mode Exit fullscreen mode

In all other cases, especially calculations or API inputs, null should be handled explicitly so that missing data is not silently turned into incorrect business logic.

6. Chaining ?. Without a Final Guard

The safe navigation operator ?. is convenient, but chaining it silently carries null through your code until it hits something that requires a non-null which is often a widget, a map widget, or an API call.

// ❌ lat and lng are double? — passed to a function expecting double
// No crash, but moveCamera silently does nothing or behaves unexpectedly
final lat = order?.rider?.location?.lat;
final lng = order?.rider?.location?.lng;
moveCamera(lat, lng); // What does your map do with null coordinates?

// ❌ Setting a StreamController that might be null — silent no-op
viewModel.order?.id?.let((id) => trackingService.subscribe(id));

Enter fullscreen mode Exit fullscreen mode

The rule: once you've chained ?., validate the final result before use.

// ✅ Validate before use
final lat = order?.rider?.location?.lat;
final lng = order?.rider?.location?.lng;

if (lat == null || lng == null) {
  // Show "locating rider..." or return
  return;
}
moveCamera(lat, lng);

Enter fullscreen mode Exit fullscreen mode

For deeply nested optional objects that you access frequently, consider a dedicated getter on the model that handles the chain in one place:

extension OrderLocation on Order? {
  LatLng? get riderLatLng {
    final lat = this?.rider?.location?.lat;
    final lng = this?.rider?.location?.lng;
    if (lat == null || lng == null) return null;
    return LatLng(lat, lng);
  }
}

// Call site is clean and explicit
final position = currentOrder.riderLatLng;
if (position == null) return;
moveCamera(position);

Enter fullscreen mode Exit fullscreen mode

7. Treating nullable booleans as binary values

A bool? is not just true or false. It has three possible states: true, false, and null. The null value usually represents “unknown” or “not yet loaded.” When you use ?? false, you collapse these states into one, and the UI loses important meaning.

❌ Unsafe approach: hiding the unknown state

// ❌ "Not verified yet" and "explicitly unverified" look the same
if (user.isVerified ?? false) {
  showVerifiedBadge();
}

// ❌ A loading or unknown state is treated as false
ElevatedButton(
  onPressed: (viewModel.canProceed ?? false) ? onPressed : null,
)

Enter fullscreen mode Exit fullscreen mode

The issue here is not just correctness, but ambiguity. The UI can no longer distinguish between:

  • The user is not verified
  • The verification status has not been loaded yet

These are different states, but they render the same.

The fix depends on what null actually means

Case 1: null means data is still loading

In this case, you should reflect that explicitly in the UI.

// ✅ Show loading state when value is unknown
switch (user.isVerified) {
  case null:
    return const CircularProgressIndicator();
  case true:
    return const VerifiedBadge();
  case false:
    return const UnverifiedBadge();
}

Enter fullscreen mode Exit fullscreen mode

Case 2: null means a distinct business state

If null represents something meaningful like “unknown” or “not applicable,” then it should be modeled explicitly instead of relying on nullable booleans.

// ✅ Explicit model removes ambiguity
enum VerificationStatus { verified, unverified, unknown }

class User {
  final VerificationStatus verificationStatus;

  User({this.verificationStatus = VerificationStatus.unknown});
}

Enter fullscreen mode Exit fullscreen mode

Now the UI becomes self explanatory and type safe.

When is ?? false acceptable

Using ?? false is only safe when null and false are truly equivalent in meaning. For example, feature flags where absence means disabled.

Even then, that decision should be intentional, not automatic.

Nullable booleans are not just a convenience type. They represent a third state that often carries important meaning. Flattening them into true or false too early can hide real application states and lead to incorrect UI behavior.

Null safety ensures the compiler flags potential null issues, but it is still your responsibility to define what null means in the context of your application. Good null handling shifts that responsibility from “making the code compile” to “making the intent clear.” Your code should communicate to the next developer and your future self what the absence of a value represents and how it should be handled.

When you reach for ?? '', !, or long chains of ?., it is worth stopping to ask a simple question: what does null represent here, and is this fallback preserving or destroying that meaning?

That moment of clarity is where most null related technical debt is either introduced or avoided.