Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
149824 stories
·
33 followers

Windows 11 Notepad now lets you create tables like in Microsoft Word, and it’s free

1 Share

Windows 11’s Notepad now lets you generate tables, which means you can create tidy notes, book lists, or to-do sheets. Tables support in Notepad is rolling out, and it will become available for everyone in the coming weeks.

Unlike Microsoft Word, the table in Notepad for Windows 11 is actually very easy to use. Once a table has been inserted, you just need to right-click on it to add, select or delete a column or row.

Tables are part of the new formatting options in Notepad

Insert a table in Notepad

Earlier this year, Notepad was updated with rich text formatting support. This allows you to format texts in Notepad using tools like bold, italic, underline, bullet points, etc. Now, Microsoft is adding support for tables.

As someone who uses Microsoft Word daily, I actually found Notepad’s table integration a far easier option. To add a table, you just need to use the new Table button on the formatting toolbar.

When you click it, you will see a small pop-up that lets you choose how many columns and rows you want, for example, 3×4. If you need a specific size, Notepad can also show the “Insert table” dialog, where you can type the exact number of columns and rows, then press Insert.

Table with text formatting in Notepad

Notepad has advanced formatting support, which means it now understands Markdown-style tables too. In the above case, I created a simple table that compares Windows 11 with Windows 10, and I’ve used formatting options like bold, italic and even hyperlink within the table.

Edit table in Notepad

You can easily edit tables by just right-clicking anywhere. Or just use the table menu in the toolbar. For example, you can edit a table and insert or delete rows and columns, or remove the whole table.

Select Table in Notepad for Windows 11

You can also quickly select a whole row, column, or the entire table, and there is an option to “Fit columns to window width.” This is my favourite feature as it allows the Table to stretch to the width of the Notepad.

Delete table in Notepad

Does it hurt performance?

Notepad is not as simple as it used to be. It has got text formatting, which we like, but at the same time, it keeps adding new AI features. You might wonder if these features add up to performance issues. In our tests, Windows Latest observed that formatting or table integration in Notepad is lightweight, and it doesn’t hurt the performance.

However, some of you may not like having many options in Notepad, as that may defeat the purpose of a ‘simple’ text editor. Thankfully, you can turn off most features in Notepad.

As you can see in the above screenshot, if you click the cog icon to open Settings, you should be able to turn off most of the features. This includes formatting, which removes bold, italic, or table options from the toolbar. In fact, you can also turn off Notepad’s ability to recover recently edited files.

Notepad has Copilot for those who pay for Microsoft 365 Copilot, and it can be disabled from the Settings. But remember that these toggles just ‘hide’ features in the app. This would mean the AI remains integrated into Notepad, but it’s not pinging Microsoft’s AI servers unless you turn on the feature.

The post Windows 11 Notepad now lets you create tables like in Microsoft Word, and it’s free appeared first on Windows Latest

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Cursor 2.0 IDE Is Now Supercharged With AI and I’m Impressed

1 Share

The Cursor IDE has been recently updated to version 2.0, and it carries with it some powerful AI integration. This new update landed at the end of October, and it includes a new feature called Composer, which is a frontier model that is purported to be four times faster than similar models.

The new version has been built for low-latency agentic coding within Cursor and was trained with several tools, including codebase-wide semantic search. This training makes Cursor much more capable of understanding and working with larger codebases.

Cursor 2.0 also has a new, cleaner interface, so it should be even easier to hit the ground running. You’ll also find plenty of agents to enable/disable, such as:

  • Composer 1
  • Sonnet 4.5
  • Gemini 3 Pro
  • GPT-5.1 Codex High
  • GPT-5.1
  • GPT-5.1 Codex Mini
  • Grok Code
  • Gemini 2.5 Flash
  • Deepseek V3.1
  • Ollama
  • GPT-5.1 Codex Fast

The above list is just scratching the surface (Figure 1).

Figure 1: As you can see, there are tons of agents that can be used with Cursor 2.0.

Other features found in Cursor 2.0 include:

  • Multiple agents (up to 8) can be run in parallel.
  • Combined diff view when using multiple agents.
  • New dedicated Agent view.
  • Support for multistep coding tasks.
  • Integrated Chrome DevTools.
  • Seamless switching between integrated and regular Chrome browser.
  • Teams can now define and share custom commands and rules to streamline workflows.
  • Built-in speech-to-text functionality.
  • Enhanced language-specific features to make it easier to navigate and debug code.
  • Shell commands run in a secure, sandboxed environment.

I decided to give Cursor 2.0 a try and see how well it works. I used an old Python script I wrote for rolling D&D dice to see how well the agent could improve the code (and how the code functions).

The problem I had with the original code was the format for the dice to be rolled. Originally, the format was like “3d6+2,” which meant roll three six-sided dice and add two to the total. But what if I wanted to simply roll a single die or add a negative modifier? Maybe I want to notate the dice roll with spaces, such as “3 d 6 + 2”? There are all sorts of permutations for dice-rolling input formats, and my original code couldn’t handle them.

Before I get to that, let’s talk about installing and setting up Cursor.

Installing Cursor

This is actually quite easy. If you’re using macOS or Windows, simply download the associated installer file, double-click it, and walk through the installation wizard. Simple.

For Linux, you’ll need to download the required installer (such as .deb or .rpm). If your distribution is set up properly, you might be given the option to open the file with your default app store. If not, let the file download complete and run the installation command, such as:

Once the installation is completed, you should find a Cursor launcher in your desktop menu. Launch the app and get ready to rock.

Configuring Cursor

Cursor works right out of the box. In fact, I opened my Python project, opened the agent, typed my query, and let it do its thing.

However, you might want to take care of a few configurations. To access the settings, click the gear icon near the top right of the main window. This will open a new tab, where you will see all the available options.

Click the Agents tab, and you can customize a few options, such as:

  • Default mode (Agent, Plan, Ask, Last used mode).
  • Default location (Editor or Pane).
  • Text size (Small, Default, Large, Extra Large).
  • Auto-Clear Chat (enable/disable).
  • Max Tab Count.
  • Queue Messages (Send immediately, Send after current message, Stop & send right away).
  • Usage Summary (Auto, Always, Never).
  • Custom Mode (enable/disable) — this feature is in beta.
  • And more.

Next, you should go to the Models tab, where you can enable or disable any/all models you want to use. At the bottom of that tab, you can also add various API keys (if necessary), such as OpenAPI, Anthropic, Azure OpenAI and more. You can also set up a Cloud Agent, which requires a folder that contains a Git repository.

After going through the various options, close the Settings tab, and you’re ready to go.

Working on My Python Project

After opening the project (which was saved on local storage), I first ran the app (by clicking the right-pointing arrow near the top of the editor pane) to make sure the app still ran. When I attempted to use it, I remember why I wanted to fix the input format because I wound up with an error.

With the project open, I clicked the Agent button near the top right corner (looks like an arrow pointing up right) and then queried:

That was all I said.

Cursor then set its agents to work on improving the script. It took Cursor about two minutes to fix the problem. Here’s what the agents reported that they did (Figure 2):

  • Simplified single die notation: You can now use “d6” instead of “1d6” — it defaults to one die.
  • Spaces allowed: Formats like “3 d 6 + 2” or “2d10 – 1” work (spaces are ignored).
  • Case insensitive: “3D6+2” works the same as “3d6+2.”
  • Better error messages: More descriptive errors when the format is invalid.
  • More robust parsing: Uses regex for more reliable parsing.

Figure 2: The Cursor agents did a great job improving my original code.

I then reran the Python app to see if Cursor really did improve the script and, lo and behold, it did. I could enter multiple formats for dice, and it worked to perfection.

What probably would have taken me more than an hour to do took Cursor a couple of minutes.

That alone is worth the price of entry.

Speaking of which …

Cursor Pricing

Here’s how the Cursor pricing breaks down:

  • Hobby (Free): One-week Pro trial, limited agent requests and limited tab completions.
  • Pro ($20/mo): Everything in Hobby plus extended limits on agent, unlimited tab completions, background agents and maximum context windows.
  • Pro+ ($60/mo): Everything in Pro plus three times usage on all OpenAI, Claude and Gemini models.
  • Ultra ($200/mo): Everything in Pro+ plus 20 times usage on all OpenAI, Claude and Gemini models, and priority access to new features.

If you like the idea of having AI agents help improve your code, do yourself a favor and give Cursor 2.0 a try.

The post Cursor 2.0 IDE Is Now Supercharged With AI and I’m Impressed appeared first on The New Stack.

Read the whole story
alvinashcraft
2 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Azure Skilling at Microsoft Ignite 2025

1 Share

The energy at Microsoft Ignite was unmistakable. Developers, architects, and technical decision-makers converged in San Francisco to explore the latest innovations in cloud technology, AI applications, and data platforms. Beyond the keynotes and product announcements was something even more valuable: an integrated skilling ecosystem designed to transform how you build with Azure.

This year Azure Skilling at Microsoft Ignite 2025 brought together distinct learning experiences, over 150+ hands-on labs, and multiple pathways to industry-recognized credentials—all designed to help you master skills that matter most in today's AI-driven cloud landscape.

Just Launched at Ignite

Microsoft Ignite 2025 offered an exceptional array of learning opportunities, each designed to meet developers anywhere on the skilling journey. Whether you joined us in-person or on-demand in the virtual experience, multiple touchpoints are available to deepen your Azure expertise. Ignite 2025 is in the books, but you can still engage with the latest Microsoft skilling opportunities, including:

  • The Azure Skills Challenge provides a gamified learning experience that lets you compete while completing task-based achievements across Azure's most critical technologies. These challenges aren't just about badges and bragging rights—they're carefully designed to help you advance technical skills and prepare for Microsoft role-based certifications. The competitive element adds urgency and motivation, turning learning into an engaging race against the clock and your peers.
  • For those seeking structured guidance, Plans on Learn offer curated sets of content designed to help you achieve specific learning outcomes. These carefully assembled learning journeys include built-in milestones, progress tracking, and optional email reminders to keep you on track. Each plan represents 12-15 hours of focused learning, taking you from concept to capability in areas like AI application development, data platform modernization, or infrastructure optimization.
  • The Microsoft Reactor Azure Skilling Series, running December 3-11, brings skilling to life through engaging video content, mixing regular programming with special Ignite-specific episodes. This series will deliver technical readiness and programming guidance in a livestream presentation that's more digestible than traditional documentation. Whether you're catching episodes live with interactive Q&A or watching on-demand later, you’ll get world-class instruction that makes complex topics approachable.

Beyond Ignite: Your Continuous Learning Journey

Here's the critical insight that separates Ignite attendees who transform their careers from those who simply collect swag: the real learning begins after the event ends. Microsoft Ignite is your launchpad, not your destination.

Every module you start, every lab you complete, and every challenge you tackle connects to a comprehensive learning ecosystem on Microsoft Learn that's available 24/7, 365 days a year. Think of Ignite as your intensive immersion experience—the moment when you gain context, build momentum, and identify the skills that will have the biggest impact on your work. What you do in the weeks and months following determines whether that momentum compounds into career-defining expertise or dissipates into business as usual.

For those targeting career advancement through formal credentials, Microsoft Certifications, Applied Skills and AI Skills Navigator, provide globally recognized validation of your expertise. Applied Skills focus on scenario-based competencies, demonstrating that you can build and deploy solutions, not simply answer theoretical questions. Certifications cover role-based scenarios for developers, data engineers, AI engineers, and solution architects. The assessment experiences include performance-based testing in dedicated Azure tenants where you complete real configuration and development tasks. And finally, the NEW AI Skills Navigator is an agentic learning space, bringing together AI-powered skilling experiences and credentials in a single, unified experience with Microsoft, LinkedIn Learning and GitHub – all in one spot

Why This Matters: The Competitive Context

The cloud skills race is intensifying. While our competitors offer robust training and content, Microsoft's differentiation comes not from having more content—though our 1.4 million module completions last fiscal year and 35,000+ certifications awarded speak to scale—but from integration of services to orchestrate workflows.

Only Microsoft offers a truly unified ecosystem where GitHub Copilot accelerates your development, Azure AI services power your applications, and Azure platform services deploy and scale your solutions—all backed by integrated skilling content that teaches you to maximize this connected experience.

When you continue your learning journey after Ignite, you're not just accumulating technical knowledge. You're developing fluency in an integrated development environment that no competitor can replicate. You're learning to leverage AI-powered development tools, cloud-native architectures, and enterprise-grade security in ways that compound each other's value. This unified expertise is what transforms individual developers into force-multipliers for their organizations.

Start Now, Build Momentum, Never Stop

Microsoft Ignite 2025 offered the chance to compress months of learning into days of intensive, hands-on experience, but you can still take part through the on-demand videos, the Global Ignite Skills Challenge,  visiting the GitHub repos for the /Ignite25 labs,  the Reactor Azure Skilling Series, and the curated Plans on Learn provide multiple entry points regardless of your current skill level or preferred learning style.

But remember: the developers who extract the most value from Ignite are those who treat the event as the beginning, not the culmination, of their learning journey. They join hackathons, contribute to GitHub repositories, and engage with the Azure community on Discord and technical forums.

The question isn't whether you'll learn something valuable from Microsoft Ignite 2025-that's guaranteed. The question is whether you'll convert that learning into sustained momentum that compounds over months and years into career-defining expertise. The ecosystem is here. The content is ready. Your skilling journey doesn't end when Ignite does—it accelerates.

 

Read the whole story
alvinashcraft
2 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Theming and Customization in Flutter: A Handbook for Developers

1 Share

Design is not just about how something looks. In product engineering, design shapes how an experience feels, how users interact with it, and how consistently the brand comes alive across every screen.

Flutter provides powerful tools for this, but true theming mastery goes far beyond changing a few colors or fonts. It involves building a unified design language, applying it predictably across components, managing scale, and ensuring the UI remains accessible, performant, and maintainable as the product grows across mobile, web, and desktop.

This handbook is for engineers and product teams who want to build serious, production-grade Flutter applications with design excellence at the core. It moves past basic theming and dives into the architecture behind robust theme systems, from Material 3 ColorSchemes, typography, and elevation systems, to advanced custom theme extensions, reusable style managers, component-level overrides, runtime theme switching, responsive strategies, and accessibility principles.

We’ll discuss and examine real-world patterns and complete code examples, and I’ll provide clear explanations of why each decision matters in practical engineering environments.

By the end, you will not only understand how Flutter theming works, but you’ll also be equipped to architect a scalable, brand-driven design system, adapt it to your product’s identity, and consistently deliver interfaces that look intentional, perform well, and feel delightful everywhere they run.

Table of Contents

  1. Prerequisites

  2. What “Theme” Means in Flutter and Why it Matters

  3. ThemeData and the Inheritance Model

  4. The Transition from Manual Color Fields to ColorScheme

  5. Typography, Text Scale, and Accessibility

  6. Component Themes and Their Importance

  7. MaterialStateProperty and State-dependent Styling

  8. Theme Extensions for Custom Design Tokens

  9. Accessing Theme Values from Widgets and Avoiding Common Pitfalls

  10. Local Overrides with the Theme Widget

  11. Runtime Theme Switching and Persistence

  12. Engineering a Robust Theme System

  13. Advanced Examples

  14. Expanding the Idea of a Theme System Beyond ThemeData

  15. Fine-Tuning: The Details That Matter

  16. Deconstructing a Real-World Flutter Theme

  17. Practical advice on structuring theme code in a project

  18. Common mistakes and how to avoid them

  19. Migrating an existing app to a proper theme system

  20. Conclusion

Prerequisites

To fully grasp the concepts and examples presented here, it helps to have a solid foundation in Flutter development. You should have the Flutter SDK installed and configured, running the latest stable version.

Familiarity with basic Dart programming, including syntax, classes, objects, and asynchronous operations using async and await is essential. A fundamental understanding of Flutter widgets, specifically StatelessWidget, StatefulWidget, the widget tree, and core components like MaterialApp and Scaffold, will be very beneficial.

Also, knowing the basics of state management through setState is crucial. A conceptual understanding of more advanced patterns like ChangeNotifier and Provider will also help you comprehend how dynamic theming works in practice.

Finally, having an integrated development environment (IDE) such as Visual Studio Code or Android Studio will facilitate the development process.

What “Theme” Means in Flutter and Why it Matters

A theme in Flutter is essentially the centralized definition of visual design tokens and component defaults that widgets can inherit. Themes allow you to express brand identity, provide consistent spacing and typography, support dark mode, and separate styling from business logic.

Themes minimize duplication and make sweeping visual updates easy. When an app scales, the theme becomes the single source of truth for colors, typography, shapes, elevations, component styles, and custom design tokens. Understanding this system is essential if you want to build maintainable, accessible, and easily brandable Flutter apps.

ThemeData and the Inheritance Model

ThemeData is the primary object you will assemble and supply to the MaterialApp widget to define an app’s look and feel. Think of it as an immutable configuration object that contains fields for colors, text themes, component themes, and more.

A diagram of a Widget Tree. At the very top is "MaterialApp (ThemeData)". Arrows flow downward to child widgets like "Scaffold", "AppBar", and "FloatingActionButton", illustrating that styles flow down like a waterfall

When you place a ThemeData on the widget tree, descendant widgets can read it using Theme.of(context). Even better, many standard Material widgets automatically consult the current Theme to determine how to draw themselves. If you need to override styles for a specific section of your app, you can place a Theme widget deeper in the tree, which overrides the inherited ThemeData for its subtree.

Here is a minimal example:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primaryColor: Colors.blue,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        textTheme: TextTheme(
          bodyMedium: TextStyle(fontSize: 16, height: 1.4),
          headlineLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(padding: EdgeInsets.all(16)),
        ),
      ),
      home: HomePage(),
    );
  }
}

This snippet shows a minimal app where ThemeData sets a primary color, a seed-based ColorScheme, text theme values, and an ElevatedButton theme. These values flow to descendant widgets, so buttons, text, and other components use the same design tokens without repeated local styling.

The Transition from Manual Color Fields to ColorScheme

In the past, developers often set color fields like primaryColor and accentColor directly. But ColorScheme is now the modern, recommended way to express an app’s color system in Flutter, aligning with Material Design. You should populate a ColorScheme and let ThemeData harmonize widget colors from those canonical tokens.

ColorScheme contains semantic color roles such as primary, onPrimary, background, surface, error, and their “on” counterparts. These roles describe how colors should be used and paired to ensure a readable UI.

A graphic showing a palette of colors labeled with semantic roles. For example, a Blue box labeled "Primary" with white text inside it labeled "OnPrimary", and a Red box labeled "Error" with white text labeled "OnError".

final colorScheme = ColorScheme.fromSeed(seedColor: Color(0xFF0066CC));

final theme = ThemeData.from(colorScheme: colorScheme).copyWith(
  useMaterial3: true,
);

The code above generates a complete ColorScheme from a seed color and builds a ThemeData from it. This enables Material 3 component defaults when useMaterial3 is set to true. Creating a theme this way makes color decisions consistent and material-compliant across components.

Material 2 vs Material 3

Material 3 (M3) introduces updated component styles, tonal palettes, and surface behaviors. In Flutter, you can enable the Material 3 look-and-feel by setting useMaterial3: true in your ThemeData.

M3 is especially relevant when using ColorScheme.fromSeed because it utilizes tonal palettes and dynamic color capabilities on supported platforms. When migrating from Material 2 to Material 3, be aware that some components have different defaults and slightly different APIs. It’s a good idea to verify key components like AppBar, Buttons, and Navigation components during the migration process.

A side-by-side comparison image. Left side: "Material 2" showing a sharp, shadowed AppBar and rectangular buttons. Right side: "Material 3" showing a flat, tinted AppBar and pill-shaped buttons.

Typography, Text Scale, and Accessibility

Just as you systemize colors, you should systemize text. TextTheme holds typographic styles mapped to semantic roles, such as displayLarge, headlineLarge, bodyMedium, and labelSmall.

You can use these semantic text roles throughout your app rather than hardcoding TextStyle values. This approach allows you to rely on MediaQuery.textScaleFactor and DefaultTextStyle to honor user-preferred font scaling automatically.

For accessible typography, make sure you use relative sizing between headlines and body text, avoid absolute pixel-perfect fonts, and target legible contrast with background surfaces.

final textTheme = TextTheme(
  headlineLarge: GoogleFonts.inter(fontSize: 32, fontWeight: FontWeight.w700),
  bodyMedium: GoogleFonts.inter(fontSize: 16, height: 1.5),
);

This text theme uses a web font via GoogleFonts (an example package) and defines headline and body scales. Using semantic TextTheme names encourages consistent typography usage across widgets and supports dynamic text scaling.

Component Themes and Their Importance

While global colors and fonts are important, sometimes you need specific control over individual widgets. Component themes allow you to define the default appearance for built-in Material widgets. Some examples include:

  • AppBarTheme

  • ElevatedButtonThemeData

  • InputDecorationTheme

  • CheckboxThemeData

  • CardTheme

  • BottomNavigationBarThemeData

Defining component themes centralizes styles like padding, shape, elevation, and color for that component type.

final theme = ThemeData(
  elevatedButtonTheme: ElevatedButtonThemeData(
    style: ButtonStyle(
      backgroundColor: MaterialStateProperty.resolveWith((states) {
        if (states.contains(MaterialState.disabled)) return Colors.grey.shade400;
        return Colors.blue;
      }),
      padding: MaterialStateProperty.all(EdgeInsets.symmetric(vertical: 14, horizontal: 20)),
      shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
    ),
  ),
  inputDecorationTheme: InputDecorationTheme(
    filled: true,
    fillColor: Colors.grey.shade100,
    contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
    border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
  ),
);

The ElevatedButtonThemeData in this snippet uses MaterialStateProperty to resolve background colors for different states, and InputDecorationTheme sets defaults for text fields. Component themes let you avoid repeating style logic in each widget instance.

MaterialStateProperty and State-dependent Styling

You may have noticed MaterialStateProperty in the previous example. This is a powerful pattern that allows you to define different style values for widget states like hovered, pressed, focused, and disabled. You can use MaterialStateProperty.resolveWith to return appropriate values based on the current state set.

An illustration of a single button shown in three different ways. 1. Default (Blue), 2. Hovered (Lighter Blue), 3. Disabled (Grey). Arrows point from the states to the button visuals.

ButtonStyle myStyle() {
  return ButtonStyle(
    overlayColor: MaterialStateProperty.resolveWith((states) {
      if (states.contains(MaterialState.pressed)) return Colors.blue.withOpacity(0.12);
      if (states.contains(MaterialState.hovered)) return Colors.blue.withOpacity(0.06);
      return null;
    }),
  );
}

This example produces overlay colors for pressed and hovered states, enabling consistent interactive feedback across buttons and similar controls by centralizing the logic.

Theme Extensions for Custom Design Tokens

Sometimes, the standard Material theme fields aren't enough for your specific design system. ThemeExtension is the official way to add bespoke design tokens to ThemeData while keeping them type-safe and consistent for animation. You can use ThemeExtension to store values such as brand radii, spacing scales, custom color palettes, or animation durations.

@immutable
class AppSpacing extends ThemeExtension<AppSpacing> {
  final double small;
  final double medium;
  final double large;

  const AppSpacing({required this.small, required this.medium, required this.large});

  @override
  AppSpacing copyWith({double? small, double? medium, double? large}) {
    return AppSpacing(
      small: small ?? this.small,
      medium: medium ?? this.medium,
      large: large ?? this.large,
    );
  }

  @override
  AppSpacing lerp(ThemeExtension<AppSpacing>? other, double t) {
    if (other is! AppSpacing) return this;
    return AppSpacing(
      small: lerpDouble(small, other.small, t)!,
      medium: lerpDouble(medium, other.medium, t)!,
      large: lerpDouble(large, other.large, t)!,
    );
  }
}

This ThemeExtension defines three spacing tokens and implements copyWith and lerp so Flutter can animate between theme instances. Adding ThemeExtension instances to ThemeData.extensions makes them available through Theme.of(context).extension().

Accessing Theme Values from Widgets and Avoiding Common Pitfalls

Now that you have defined your theme, you need to know how to use it. Accessing theme data allows your custom widgets to adapt automatically to changes in the app's look and feel – but timing is everything.

You can call Theme.of(context) inside build methods to access ThemeData or use context.read-style helpers in platforms offering extensions. But you should avoid calling Theme.of(context) during initState. At that stage, the widget tree’s inherited widgets may not be available yet. Instead, you can call it in didChangeDependencies or inside a post-frame callback.

@override
void didChangeDependencies() {
  super.didChangeDependencies();
  final textTheme = Theme.of(context).textTheme;
  // Use textTheme for initial logic that depends on theme values.
}

Using didChangeDependencies ensures the inherited themes are ready and avoids null or stale values that could occur in initState.

Local Overrides with the Theme Widget

Occasionally, you might want a specific section of your app (a subtree) to use a modified theme without changing the global theme. You can wrap that subtree with a Theme widget and use copyWith to change only the fields needed.

Theme(
  data: Theme.of(context).copyWith(
    colorScheme: Theme.of(context).colorScheme.copyWith(primary: Colors.green),
  ),
  child: SomeLocalWidget(),
)

This code temporarily swaps the primary color for the SomeLocalWidget subtree, leaving the rest of the app unaffected. Local overrides are useful for dialogs, special sections, or branded components.

Runtime Theme Switching and Persistence

A truly modern app usually allows users to toggle between light and dark modes or choose custom themes. You can implement runtime switching by driving ThemeMode through a top-level state management solution like Provider, Riverpod, Bloc, or an inherited ValueNotifier.

Then, you can persist the user’s choice with SharedPreferences, secure storage, or app-level persistence so the preference survives restarts.

pair of screenshots showing the exact same screen in "Light Mode" and "Dark Mode", illustrating how the colors invert based on the theme toggle.

class ThemeController with ChangeNotifier {
  ThemeMode _mode = ThemeMode.system;
  ThemeMode get mode => _mode;

  Future<void> load() async {
    final prefs = await SharedPreferences.getInstance();
    final index = prefs.getInt('themeMode') ?? 2;
    _mode = ThemeMode.values[index];
    notifyListeners();
  }

  Future<void> setMode(ThemeMode mode) async {
    _mode = mode;
    notifyListeners();
    final prefs = await SharedPreferences.getInstance();
    prefs.setInt('themeMode', mode.index);
  }
}

The ThemeController wraps ThemeMode and persists it to SharedPreferences. You can merge this with a ChangeNotifierProvider at the app root to rebuild MaterialApp with the chosen ThemeMode.

Engineering a Robust Theme System

With the foundation in place, the next step is turning your theme setup into a fully engineered system that can support a real product. A production-ready theme system must be able to handle smooth visual transitions, integrate correctly with the operating system, maintain high performance, and meet accessibility expectations.

The subsections that follow break down each of these areas and show how to design a theme system that scales cleanly across platforms and product requirements.

AnimatedTheme for Smooth Transitions

When a user switches themes, you don't want the colors to snap instantly. You can use AnimatedTheme to animate visual transitions when ThemeData changes during runtime. This provides user-friendly fading and interpolation of theme-dependent properties.

AnimatedTheme(
  data: currentThemeData,
  duration: Duration(milliseconds: 300),
  child: MaterialApp(
    theme: lightThemeData,
    darkTheme: darkThemeData,
    themeMode: themeController.mode,
    home: HomePage(),
  ),
)

AnimatedTheme listens for changes in currentThemeData and automatically animates the transition between the old theme and the new one. The duration controls how long the fade takes, and the MaterialApp inside still provides the light theme, dark theme, and theme mode. When the theme updates, the entire app smoothly transitions instead of switching abruptly.

Platform Brightness and System Integration

Your app should ideally respect the user's OS settings. MaterialApp accepts theme, darkTheme, and themeMode parameters. You can count on themeMode: ThemeMode.system to adapt to OS-level dark mode preferences automatically.

For fine-grained control or for platforms where you want to detect brightness directly, you can use MediaQuery.platformBrightness or WidgetsBinding.instance.window.platformBrightness.

final brightness = MediaQuery.platformBrightnessOf(context);
if (brightness == Brightness.dark) {
  // adjust local behavior if necessary
}

Dynamic Color (Android 12+)

Android 12 introduced dynamic color based on the user's wallpaper. Flutter exposes this for Material 3 via the dynamic_color package and ColorScheme.fromSeed.

// pseudo-code sketch; dynamic_color package usage is similar
final corePalette = await DynamicColorPlugin.getCorePalette();
final colorScheme = ColorScheme.fromSeed(seedColor: Color(corePalette.primary.value));

This allows your app to feel native on devices with wallpaper-based theming.

Performance Considerations

From a performance standpoint, avoid rebuilding the entire widget tree when only a small subtree needs a theme change. You can use local Theme overrides for smaller changes and const constructors wherever possible.

You should also avoid recalculating complex theme values in build methods. Just compute them once and store them if static. While accessing Theme.of(context) is inexpensive, avoid using it in tight render loops. You can cache values if a widget rebuilds frequently.

Accessibility, Contrast, and Color Blindness

A good theme is an accessible one. So you’ll want to make sure that contrast ratios meet WCAG AA or AAA when required. You can use tools to calculate contrast between text and background colors.

You should also provide high-contrast theme variants and respect platform-level accessibility options like high-contrast mode. It’s also a good idea to use semantics and proper labels for color-only indicators, and avoid conveying information with color alone.

RTL and Localization

Directionality influences certain widgets and layouts. Theme tokens generally remain direction-agnostic, but you should be mindful of shapes that mirror horizontally. Use Directionality and Localizations to adapt any theme-driven layout decisions that depend on language or cultural conventions.

Theming and Testing

Finally, you should verify your theme logic with tests. Write golden tests and widget tests that render your widgets under both light and dark themes.

testWidgets('MyCard respects theme', (tester) async {
  final theme = ThemeData.light().copyWith(cardTheme: CardTheme(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))));
  await tester.pumpWidget(MaterialApp(home: Theme(data: theme, child: MyCard())));
  // Add assertions for shape, text style, etc.
});

The test sets a custom Theme for the widget and then uses assertions to ensure the widget respects theme values.

Debugging with DevTools

If you run into issues, the Flutter DevTools inspector shows the widget tree and applied styles. You can use it to visualize inherited ThemeData, see where a specific style comes from, and detect unexpected overrides.

Advanced Examples

Now that we have covered the concepts and engineering considerations, let's look at how to structure a complete theme solution.

Seed-Based Root Theme with Custom Extensions

This pattern defines a central theme class that generates both light and dark themes from the same seed color and attaches custom extensions for shared design tokens.

class MyTheme {
  static final lightColorScheme = ColorScheme.fromSeed(seedColor: Color(0xFF6750A4), brightness: Brightness.light);
  static final darkColorScheme = ColorScheme.fromSeed(seedColor: Color(0xFF6750A4), brightness: Brightness.dark);

  static ThemeData lightTheme() {
    return ThemeData(
      colorScheme: lightColorScheme,
      useMaterial3: true,
      textTheme: TextTheme(bodyMedium: TextStyle(fontSize: 16)),
      extensions: [const AppSpacing(small: 8, medium: 12, large: 24)],
    );
  }

  static ThemeData darkTheme() {
    return ThemeData(
      colorScheme: darkColorScheme,
      useMaterial3: true,
      textTheme: TextTheme(bodyMedium: TextStyle(fontSize: 16)),
      extensions: [const AppSpacing(small: 8, medium: 12, large: 24)],
    );
  }
}

This class builds consistent light and dark ThemeData objects from a shared seed color using Material 3’s dynamic color generation. It also includes a custom AppSpacing extension, allowing your app to use reusable spacing tokens directly through the theme.

Runtime Theme Switching with ValueListenableBuilder

This pattern uses a ValueNotifier to track the active ThemeMode and rebuilds the app whenever the user toggles between light and dark themes, while AnimatedTheme provides a smooth transition.

class ThemeToggleApp extends StatefulWidget {
  @override
  State<ThemeToggleApp> createState() => _ThemeToggleAppState();
}

class _ThemeToggleAppState extends State<ThemeToggleApp> {
  final ValueNotifier<ThemeMode> _mode = ValueNotifier(ThemeMode.system);

  @override
  void dispose() {
    _mode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<ThemeMode>(
      valueListenable: _mode,
      builder: (context, mode, child) {
        return AnimatedTheme(
          data: mode == ThemeMode.dark ? MyTheme.darkTheme() : MyTheme.lightTheme(),
          duration: Duration(milliseconds: 300),
          child: MaterialApp(
            theme: MyTheme.lightTheme(),
            darkTheme: MyTheme.darkTheme(),
            themeMode: mode,
            home: Scaffold(
              appBar: AppBar(title: Text('Theme Toggle')),
              body: Center(
                child: ElevatedButton(
                  onPressed: () {
                    _mode.value = _mode.value == ThemeMode.dark ? ThemeMode.light : ThemeMode.dark;
                  },
                  child: Text('Toggle'),
                ),
              ),
            ),
          ),
        );
      },
    );
  }
}

ValueListenableBuilder listens to the current ThemeMode, and every time the value changes, the app rebuilds with the appropriate theme. The switch is animated through AnimatedTheme, producing a smooth fade between light and dark modes.

Expanding the Idea of a Theme System Beyond ThemeData

At production scale, a theme is rarely limited to a single ThemeData declaration inside main.dart. Instead, it becomes a layered design system.

In this system, the Flutter ThemeData object is just the final mapping layer from product tokens to widget defaults. The real system starts with design tokens from the brand or product identity, stored in internal files such as app_colors.dart, font_manager.dart, styles_manager.dart, and values_manager.dart. These files act as the canonical source for spacing, color scales, type scales, corner radius scales, motion values, opacity tokens, and shadows.

The theme maps these values into ThemeData, and ThemeData becomes the single point of truth for widgets. This layered structure prevents visual inconsistencies and makes future redesigns predictable.

An illustration of a layered pyramid. The bottom layer is labeled "Tokens (app_colors.dart)", the middle layer is "Theme Logic (app_theme.dart)", and the top layer is "Widget UI (MaterialApp)".

Practical example of token-to-theme mapping structure

To visualize this, imagine your lib folder structure. You typically have your core "manager" files that aggregate styles, and then the lower-level token files that define raw values.

lib/
  theme/
    app_theme.dart        <-- Entry point (getTheme)
    theme_manager.dart    <-- Logic layer
    styles_manager.dart   <-- Text style generators
    values_manager.dart   <-- Spacing/Sizes
    font_manager.dart     <-- Font weights/families
    app_colors.dart       <-- Raw hex codes

In this arrangement, tokens are separated from Flutter’s widget-aware theme logic. Designers update tokens while developers update the mapping once. The app updates instantly.

The Token Layer (Bottom-up)

app_colors.dart typically contains brand colors:

class AppColors {
  static const primaryColor = Color(0xFF0066CC);
  static const secondaryColor = Color(0xFF1E88E5);
  static const primarySecondaryBackground = Color(0xFFE6EEF6);
  static const darkBackground = Color(0xFF0E0E0E);
  static const lightBackground = Colors.white;
}

font_manager.dart defines type tokens:

class FontWeightManager {
  static const regular = FontWeight.w400;
  static const medium = FontWeight.w500;
  static const semiBold = FontWeight.w600;
  static const bold = FontWeight.w700;
}

class FontSize {
  static const s12 = 12.0;
  static const s14 = 14.0;
  // ... s16, s18, s22, s32
}

values_manager.dart defines spacing, radius, and elevations:

class AppSize {
  static const s4 = 4.0;
  static const s8 = 8.0;
  // ... s12, s16, s24, s32
}

class AppRadius {
  static const r8 = Radius.circular(8);
  static const r12 = Radius.circular(12);
  static const r20 = Radius.circular(20);
}

class AppElevation {
  static const level0 = 0.0;
  static const level1 = 1.0;
  static const level2 = 2.0;
  static const level4 = 4.0;
}

styles_manager.dart exposes semantic text styles:

TextStyle _getTextStyle(double size, FontWeight weight, Color color) {
  return TextStyle(fontSize: size, fontWeight: weight, color: color);
}

class AppTextStyles {
  static TextStyle headlineLarge(Color color) =>
      _getTextStyle(FontSize.s32, FontWeightManager.bold, color);

  static TextStyle bodyMedium(Color color) =>
      _getTextStyle(FontSize.s16, FontWeightManager.regular, color);
}

These files reflect a mature theme system where design logic stays separate from widget building.

Integrating these tokens into a Flutter theme

Once your tokens are defined, you’ll need to map them to ThemeData. In older or enterprise codebases that predate Material 3, you might see a pattern where a ColorScheme is generated from a swatch, followed by manual overrides for specific background or surface colors.

ThemeData getTheme() {
  return ThemeData(
    colorScheme: ColorScheme.fromSwatch()
        .copyWith(secondary: Colors.white)
        .copyWith(background: Colors.white, onBackground: Colors.white),

    primaryColor: AppColors.primaryColor,
    primaryColorLight: Colors.black,
    primaryColorDark: Colors.white,

    scaffoldBackgroundColor: Colors.white,
    disabledColor: AppColors.primarySecondaryBackground,
    dialogBackgroundColor: Colors.white,

    bottomSheetTheme: const BottomSheetThemeData(
      backgroundColor: Colors.white,
      elevation: 0,
    ),

    floatingActionButtonTheme: const FloatingActionButtonThemeData(),

    systemOverlayStyle: const SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      statusBarIconBrightness: Brightness.dark,
    ),
  );
}

The value of this approach is flexibility: you control every color explicitly. But the modern Flutter recommendation (especially for Material 3) is to migrate towards a seed-based approach.

Migrating legacy token-based themes to Material 3 seed palettes

Even when brands provide specific hex colors, you can derive tonal palettes from those tokens using ColorScheme.fromSeed:

final _seed = AppColors.primaryColor;
final lightScheme = ColorScheme.fromSeed(seedColor: _seed, brightness: Brightness.light);
final darkScheme  = ColorScheme.fromSeed(seedColor: _seed, brightness: Brightness.dark);

Then attach custom extensions:

ThemeData(
  colorScheme: lightScheme,
  useMaterial3: true,
  extensions: [
    const AppSpacing(small: 8, medium: 12, large: 24),
  ],
);

Seed palettes scale better across dark/light surfaces and accessibility constraints. Brands can keep exact color identities while gaining tonal depth and system-level harmony.

Fine-Tuning: The Details That Matter

Once the core structure is in place, the difference between a good app and a great one lies in the details – like how the app handles system UI, motion, shadows, and platform-specific norms.

System UI Overlay Styling

Status bar and system navigation bar colors impact perceived chromatic harmony. Flutter allows you to configure them via systemOverlayStyle. Keeping this inside theme code ensures your system chrome always matches your brand surfaces. If you style system overlays per-page, you risk inconsistency and unreadability.

Motion Tokens and Animation Design

Design systems include motion. Flutter lets you centralize motion tokens and interpolate them in the theme using extensions:

class MotionTokens extends ThemeExtension<MotionTokens> {
  final Duration fast;
  final Duration normal;
  final Duration slow;

  const MotionTokens({required this.fast, required this.normal, required this.slow});

  @override
  MotionTokens lerp(ThemeExtension<MotionTokens>? other, double t) {
    if (other is! MotionTokens) return this;
    return MotionTokens(
      fast: Duration(milliseconds: lerpDouble(fast.inMilliseconds.toDouble(), other.fast.inMilliseconds.toDouble(), t)!.toInt()),
      normal: Duration(milliseconds: lerpDouble(normal.inMilliseconds.toDouble(), other.normal.inMilliseconds.toDouble(), t)!.toInt()),
      slow: Duration(milliseconds: lerpDouble(slow.inMilliseconds.toDouble(), other.slow.inMilliseconds.toDouble(), t)!.toInt()),
    );
  }
}

Apps that animate layout, opacity, and elevation transitions feel more premium when these durations are consistent and theme-driven.

Gradients, Shadows, and Shapes

Design systems often require gradients and shadows. Since Flutter doesn’t have built-in gradient theme fields, you can store them in extensions:

class AppGradients {
  static const primaryGradient = LinearGradient(
    colors: [Color(0xFF0050BB), Color(0xFF3388FF)],
    begin: Alignment.topLeft,
    end: Alignment.bottomRight,
  );
}

You can then fetch these via Theme.of(context).extension<AppGradients>(). Similarly, you can standardize your shadow tokens and corner radii to ensure uniform hierarchy and curvature across the app.

Component Density and Platform Adaptation

Flutter supports adaptive density via visualDensity. On desktop you want tighter controls, while on mobile, larger touch targets.

visualDensity: VisualDensity.adaptivePlatformDensity,

You can combine this with spacing tokens to produce consistent layouts across platforms.

Cupertino and Material Cross-theming

When targeting iOS, you can build a Cupertino theme that mirrors your Material tokens. Since ThemeData does not directly style Cupertino widgets, you should use CupertinoThemeData or cross-platform components.

CupertinoThemeData(
  primaryColor: AppColors.primaryColor,
  textTheme: CupertinoTextThemeData(
    textStyle: TextStyle(fontSize: FontSize.s16, fontWeight: FontWeightManager.regular),
  ),
)

Robust Dark Mode Handling

Dark themes are not simply inverted light themes. Good dark themes adjust content elevation, accent chroma, and surface tint.

surfaceTintColor: lightScheme.surfaceTint,

You can use slightly desaturated primaries for text and icons in dark mode. Just make sure to respect user expectations and maintain contrast standards.

White-label and B2B Strategies

For products deployed to multiple clients, consider using JSON-based token ingestion.

final config = BrandConfig.fromJson(json);
return AppTheme.fromBrand(config);

Each brand receives a separate token file, but the structure remains unified.

Deconstructing a Real-World Flutter Theme

To wrap up, let's deconstruct what a real-world theme file looks like in a production app. This example demonstrates the discipline of having a single source of truth for styles, component overrides, and typography.

We’ll begin with a centralized theme entry point. This is where visual language becomes enforceable architecture:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../constants/app_colors.dart';
import 'styles_manager.dart';
import 'values_manager.dart';
import 'font_manager.dart';

// Light Dark Theme
ThemeData getTheme() {
  return ThemeData(
    // ...

Placing your theme behind a factory like getTheme() signals intent: style decisions belong here, not inside widgets.

Foundation: Color System and Background Roles

This section defines the app’s core visual identity and establishes consistent contrast across components. The colorScheme sets primary, secondary, and background colors, ensuring readability and cohesion, while properties like dialogBackgroundColor, primaryColor, and scaffoldBackgroundColor provide explicit control over key surfaces and interactive elements. This creates a predictable, visually balanced UI that aligns with your brand and supports accessibility.

colorScheme: ColorScheme.fromSwatch()
    .copyWith(
      secondary: Colors.white,
    )
    .copyWith(
      background: Colors.white,
      onBackground: Colors.white,
    ),
dialogBackgroundColor: Colors.white,
primaryColor: AppColors.primaryColor,
primaryColorLight: Colors.black,
primaryColorDark: Colors.white,
disabledColor: AppColors.primarySecondaryBackground,
scaffoldBackgroundColor: Colors.white,

Floating Action Button Identity

This section defines the visual style and behavior of all floating action buttons in the app. Using floatingActionButtonTheme, you can standardize properties such as shape, color, and elevation to ensure consistency and align the FAB with your overall design language.

floatingActionButtonTheme: FloatingActionButtonThemeData(
 // shape: const CircleBorder(),
),

Even unused configuration here matters. Declaring an explicit FAB theme ensures predictable evolution later.

Bottom Sheet Consistency

This section ensures a consistent look and feel for all bottom sheets in the app. By setting bottomSheetTheme, you can control background color, elevation, and other surface properties, making bottom sheets visually cohesive with your overall theme and reducing unexpected style variations.

bottomSheetTheme: const BottomSheetThemeData(
  backgroundColor: Colors.white,
  elevation: 0,
),

Bottom sheets often suffer from fragmentation across apps. Unifying them prevents visual drift.

Buttons: Legacy Meets Modern Structure

This section standardizes the appearance of legacy buttons across the app. ButtonThemeData lets you define default colors, shapes, and disabled states, ensuring a consistent style while bridging older button widgets with the modern Material design system.

buttonTheme: const ButtonThemeData(
  buttonColor: AppColors.primaryColor,
  shape: StadiumBorder(),
  disabledColor: AppColors.primarySecondaryBackground,
),

This is the legacy Button API. The real structure comes next with ElevatedButtonThemeData:

elevatedButtonTheme: ElevatedButtonThemeData(
  style: ElevatedButton.styleFrom(
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(AppSize.s5),
    ),
    backgroundColor: AppColors.primaryColor,
    disabledBackgroundColor: AppColors.secondaryColor,
    disabledForegroundColor: Colors.white,
    elevation: 0,
    textStyle: getRegularStyle(
      color: Colors.white,
      fontSize: FontSize.s14,
      fontWeight: FontWeightManager.normal,
    ),
  ),
),

Dialog & Date Selection UI

This section defines the visual style of dialogs and date pickers. Using DatePickerThemeData, you can customize background colors, shapes, header colors, and text styles to ensure a cohesive and polished user experience that aligns with your app’s overall theme.

datePickerTheme: DatePickerThemeData(
  backgroundColor: Colors.white,
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(12.0),
  ),
  headerBackgroundColor: AppColors.primaryColor,
  headerForegroundColor: Colors.white,
  // ...
),

Text Selection and Cursor Behavior

This section controls how text fields appear during user interaction. TextSelectionThemeData defines the cursor color, text selection highlight, and handle colors, ensuring a consistent and accessible text editing experience across the app.

textSelectionTheme: const TextSelectionThemeData(
  cursorColor: Colors.white,
  selectionColor: Colors.white38,
  selectionHandleColor: Colors.white,
),

Form Inputs and Field DNA

This section defines the core styling of all input fields in the app. InputDecorationTheme sets border styles, corner radius, colors, and icon appearances, creating a consistent “DNA” for form elements that aligns with your brand and improves usability across screens.

inputDecorationTheme: InputDecorationTheme(
  border: OutlineInputBorder(
    borderRadius: BorderRadius.circular(AppSize.s10),
    borderSide: const BorderSide(
      color: AppColors.greyShade2,
    ),
  ),
  // ...
  prefixIconColor: AppColors.greyShade1,
),

Checkbox System

This section standardizes the appearance of all checkboxes in the app. CheckboxThemeData lets you control the checkmark color, fill color, and border style, ensuring consistency, clarity, and alignment with the overall design language.

checkboxTheme: CheckboxThemeData(
  checkColor: MaterialStateProperty.all(AppColors.primaryColor),
  fillColor: MaterialStateProperty.all(AppColors.primaryFourElementText),
  side: BorderSide.none,
),

AppBar Chrome & System Layer Integration

This section defines the style and system-level behavior of app bars. AppBarTheme controls icon colors and sizes, title text style, elevation, and background transparency, while systemOverlayStyle ensures the status bar integrates seamlessly with the app’s theme, maintaining readability and visual consistency across screens.

appBarTheme: AppBarTheme(
  iconTheme: const IconThemeData(
    color: Colors.black,
    size: AppSize.s40,
  ),
  centerTitle: false,
  color: Colors.transparent,
  elevation: AppSize.s0,
  titleTextStyle: getRegularStyle(
    color: Colors.black,
    fontSize: FontSize.s18,
  ),
  systemOverlayStyle: const SystemUiOverlayStyle(
    statusBarColor: Colors.transparent,
    statusBarBrightness: Brightness.dark,
    statusBarIconBrightness: Brightness.dark,
  ),
),

Typography

This section establishes the app’s typographic system. TextTheme defines styles for different text roles, such as headings and body text, including font size, weight, and color, ensuring readable, consistent, and brand-aligned text across all screens.

textTheme: TextTheme(
  displayLarge: getMediumStyle(
    color: Colors.black,
    fontSize: FontSize.s16,
  ),
  bodySmall: getRegularStyle(
    color: Colors.black,
    fontSize: FontSize.s12,
  ),
  bodyLarge: getRegularStyle(
    color: Colors.black,
  ),
),

Practical Advice on Structuring Theme Code in a Project

It’s a good idea to organize theming as a first-class architectural concern by placing all theme code in a dedicated directory, such as lib/theme, with well-defined files like light_theme.dart, dark_theme.dart, theme_extensions.dart, and theme_factory.dart. You can encapsulate token definitions, extension classes, and mapping functions, and export a single entrypoint, app_theme.dart, for use throughout the app. You should also keep theme factories pure and deterministic to simplify testing.

A mature Flutter theme system is not merely visual – it’s also structural. It separates design intention (tokens) from implementation (ThemeData) and consumption (widgets). When done well, design can evolve without refactoring UI code. But when done poorly, every redesign becomes a rewrite.

You can build a scalable foundation by relying on ColorScheme and ThemeExtension instead of scattered styling, centralizing component themes, and supporting system, light, and dark modes with smooth transitions. You should persist user preferences, honour accessibility requirements like contrast and text scaling, and verify behavior with golden and widget tests. It’s a good idea to use Flutter DevTools to trace theme inheritance and color usage.

With a thoughtful structure and disciplined execution, your theming system becomes a resilient, future-proof design layer that scales confidently with both your app and your product vision.

Common Mistakes and How to Avoid Them

Hardcoding colors, sizes, and TextStyle values directly inside individual widgets breaks visual consistency and makes future changes costly. When you scatter color codes or font sizes across dozens of files, updating even a single brand color becomes a manual, error-prone process.

Another common issue is relying on only primaryColor without defining a full ColorScheme. Modern Material widgets depend on multiple color roles primary, secondary, surface, onSurface, outline, and others. If these fields aren’t defined properly, widgets fall back to defaults, producing inconsistent or unexpected results across screens.

Developers also run into subtle bugs by calling Theme.of(context) too early in the widget lifecycle—for example, inside object constructors or outside the widget tree. Similarly, assuming theme values automatically flow across independent Material widgets can cause confusion; inheritance only applies within the same MaterialApp and widget subtree.

To avoid these issues, adopt a theme-first approach. Define your design tokens (colors, typography scales, spacing, elevations), map them to ThemeData, ColorScheme, and any custom ThemeExtensions, and then apply overrides only where the design specifically calls for it. This guarantees consistency, reduces duplication, and keeps future updates painless.y.

Migrating an Existing App to a Proper Theme System

Start by auditing your entire app for hardcoded values: colors, font sizes, text styles, paddings, button styles, shadows, and custom widget decorations. Make a list of repeated values and patterns, then convert these into reusable theme tokens or custom extensions.

Next, create a well-structured ColorScheme that covers all Material color roles. Replace standalone color variables with this unified scheme and adjust affected widgets accordingly. Then review each Material component (AppBar, TextField, BottomNavigationBar, ElevatedButton, Card, etc.) and move local styling into their specific theme fields (appBarTheme, inputDecorationTheme, bottomNavigationBarTheme, etc.).

As you migrate, test your UI under light and dark themes, increased text scale, and different device dimensions to make sure your theme behaves responsively and consistently.

Adopt an incremental approach: start with global ThemeData (ColorScheme, Typography), then migrate core components and shared widgets, and finally refine specialized screens. This staged method avoids breaking large sections of the app at once and makes the migration easier to maintain and review.

Conclusion

Mastering theming in Flutter goes beyond just choosing colors and fonts. It’s about building a scalable visual system that evolves with your product, reinforces brand identity, improves accessibility, and ensures consistent behavior across platforms.

When done right, theming becomes a foundation rather than an afterthought that’s powerful enough to support multiple form factors, flexible enough to handle runtime customization, and structured enough to scale with your development team and feature roadmap.

As Flutter continues to mature, so will its design ecosystem, and developers who deeply understand theme architecture, extensions, Material principles, and performance considerations will be positioned to build polished, future-ready experiences. So treat your theme as a living design system – refine it with your designers, test it like core business logic, and let it guide your UI, not the other way around.

With deliberate structure and thoughtful application, your Flutter apps will not only look beautiful, but feel consistent, perform smoothly, and adapt gracefully across devices and user contexts.



Read the whole story
alvinashcraft
3 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

.NET Day on Agentic Modernization Coming Soon

1 Share

Join us on December 9, 2025 between 9AM-1PM Pacific for .NET Day of Agentic Modernization! This is a free, one-day virtual event focused on the latest tooling, techniques, and guidance for modernizing your .NET applications. Whether you’re upgrading legacy code, preparing for cloud workloads, or exploring how AI and agentic patterns fit into your architecture, this event will show you what’s possible with today’s tooling while keeping reliability, security, and developer control front and center. Buckle up because we’ll be demo heavy and you can get your questions answered live!

.NET Day on Agentic Modernization event banner

Agenda

We have 8 great sessions throughout the event that will be broadcast live. Tune in and get your questions answered from the presenters!

🚀 Choose Your Modernization Adventure with GitHub Copilot with Brady Gaster – See how GitHub Copilot and Visual Studio speed app modernization- upgrading code, fixing dependencies, and guiding secure, cloud-ready migrations into Azure.

⚙ Agentic DevOps: Enhancing .NET Web Apps with Azure MCP with Yun Jung Choi – Learn how AI-powered tooling and Azure MCP streamline .NET app development-code, storage, SQL, and IaC workflows-with faster, smarter Azure-ready delivery.

🛡 Fix It Before They Feel It: Proactive .NET Reliability with Azure SRE Agent with Deepthi Chelupati and Shamir Abdul Aziz – See how Azure SRE Agent and App Insights detect .NET regressions, automate rollbacks, and streamline incident prevention with custom agents and health checks.

☁ No‑Code Modernization for ASP.NET with Managed Instance on Azure App Service with Andrew Westgarth and Gaurav Seth – See how Azure App Service Managed Instance removes ASP.NET migration blockers-enabling fast modernization, better performance, lower TCO, and integration with modern agentic workflows.

🤖 Modernization Made Simple: Building Agentic Solutions in .NET with Bruno Capuano – Learn how to add the Agent Framework to existing .NET apps – unlocking multi-agent collaboration, memory, and tool orchestration with practical, fast-start guidance.

💪 Bulletproof Agents with the Durable Task Extension for Microsoft Agent Framework with Chris Gillum and Thiago Almeida – See how the Durable Extension for the Microsoft Agent Framework brings durable, distributed, deterministic, and debuggable AI agents to Azure—enabling reliable, scalable, production-ready agentic workflows.

🔐 Securely Unleash AI Agents on Azure SQL and SQL Server with Davide Mauri – Learn how to let AI agents work safely with Azure SQL – enforcing strict security, least-privilege access, and schema-aware techniques that prevent data leaks and query errors.

⚡ Secure and Smart AI Agents Powered by Azure Redis with Catherine Wang – See how Azure Redis powers secure, streamlined data access for .NET agentic apps – using MCP, Redis-backed tools, and modern security patterns to simplify development.

Tune in

Don’t miss this opportunity to get practical, real-world guidance on modernizing .NET applications for Azure, AI, and agentic patterns. Mark your calendars and get ready for .NET Day on Agentic Modernization!

The post .NET Day on Agentic Modernization Coming Soon appeared first on .NET Blog.

Read the whole story
alvinashcraft
3 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

It’s the 2025 Maker Shed Gift Guide!

1 Share
It’s the 2025 Maker Shed Gift Guide!

Wondering what top gifts you can find in Maker Shed? Our 2025 staff picks are here.

The post It’s the 2025 Maker Shed Gift Guide! appeared first on Make: DIY Projects and Ideas for Makers.

Read the whole story
alvinashcraft
4 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories