1

I'm making a simple 'To Do List' app In Flutter, When I press the 'floatingActionButton' it's supposed to to show a pop-up where the user can input a new task, But nothing is displayed when the button is pressed, Testing on an actual Android phone, Here's the code:

import 'package:flutter/material.dart';
import 'task.dart';

void main() => runApp(MyApp());


class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  List<String> tasks = [];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(

        appBar: AppBar(
          backgroundColor: Colors.yellow[700],
          title: Text("To Do List"),
          centerTitle: true,
        ),

        body: Container(margin: EdgeInsets.all(20), child:
          Column(children:
              tasks.map(
                  (name) => Task(name: name, delete: () => setState(()=>tasks.remove(name)))
              ).toList() as List<Widget>
          )
        ),

        floatingActionButton: IconButton(icon: Icon(Icons.add), onPressed: displayPrompt),

      )
    );
  }

  Future<void> displayPrompt() async {
    await showDialog(
        context: context,
        builder: (BuildContext context) => TextField(
          onSubmitted: (String input) => setState(()=>tasks.add(input)),
          decoration: InputDecoration(hint: Text("Please enter a task")),
          autocorrect: true,
          autofocus: true,
        )
    );
  }

}

Here's the custom Widget I made:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class Task extends StatelessWidget {

  final String name;
  final VoidCallback delete;

  const Task({required this.name, required this.delete, super.key});

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Row(children: [
        Text(name),
        IconButton(icon: Icon(Icons.delete), onPressed: delete,)
      ]),
    );
  }
1
  • 1
    try to separate the MaterialApp context in parent and return a Material widget like AlertDialog from showDialog's builder Commented Nov 17 at 21:40

2 Answers 2

5

The issue is with your showDialog call, you’re returning a bare TextField, but showDialog expects a dialog widget (like AlertDialog or Dialog).

Because the builder doesn’t return a proper Material dialog, nothing gets displayed, especially on a real Android device where Material localizations and overlays are required for dialogs to render.

You also used an IconButton as the floating action button instead of a proper FloatingActionButton, which can cause visibility or context issues.

Here’s a fixed, fully working version of your app

import 'package:flutter/material.dart';

void main() {
  runApp(const ToDoApp());
}

class ToDoApp extends StatelessWidget {
  const ToDoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'To Do List',
      theme: ThemeData(
        primarySwatch: Colors.yellow,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final List<String> tasks = [];

  void _showAddTaskDialog() {
    String newTask = "";
    showDialog(
      context: context,
      builder: (BuildContext dialogContext) {
        return AlertDialog(
          title: const Text("Add a New Task"),
          content: TextField(
            autofocus: true,
            decoration: const InputDecoration(
              hintText: "Enter your task here",
            ),
            onChanged: (value) => newTask = value,
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(dialogContext),
              child: const Text("CANCEL"),
            ),
            TextButton(
              onPressed: () {
                if (newTask.trim().isNotEmpty) {
                  setState(() => tasks.add(newTask.trim()));
                }
                Navigator.pop(dialogContext);
              },
              child: const Text("ADD"),
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("To Do List"),
        backgroundColor: Colors.yellow[700],
        centerTitle: true,
      ),
      body: tasks.isEmpty
          ? const Center(
              child: Text(
                "No tasks yet. Tap + to add one!",
                style: TextStyle(fontSize: 16, color: Colors.grey),
              ),
            )
          : ListView.builder(
              padding: const EdgeInsets.all(16),
              itemCount: tasks.length,
              itemBuilder: (context, index) {
                return Card(
                  margin: const EdgeInsets.symmetric(vertical: 6),
                  child: ListTile(
                    title: Text(tasks[index]),
                    trailing: IconButton(
                      icon: const Icon(Icons.delete, color: Colors.red),
                      onPressed: () {
                        setState(() => tasks.removeAt(index));
                      },
                    ),
                  ),
                );
              },
            ),
      floatingActionButton: FloatingActionButton(
        backgroundColor: Colors.yellow[700],
        onPressed: _showAddTaskDialog,
        child: const Icon(Icons.add),
      ),
    );
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much for pretty much writing a superior version of my app. Like you said, the issue is that I'm returning a plain Widget instead of a Material widget, But also I had to separate the MaterialApp context like @Md. Yeasin Sheikh said in another reply.
2

Try to separate the MaterialApp context and put in parent and return a Material widget like AlertDialog from showDialog's builder.

You can find more https://api.flutter.dev/flutter/material/AlertDialog-class.html

void main() => runApp(MaterialApp(home: MyApp()));

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<String> tasks = [];

  Future<void> displayPrompt() async {
    await showDialog(
      context: context,
      builder: (BuildContext context) => AlertDialog(
        content: TextField(
          onSubmitted: (String input) => setState(() => tasks.add(input)), 
          decoration: InputDecoration(hint: Text("Please enter a task")),
          autocorrect: true,
          autofocus: true,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.yellow[700],
        title: Text("To Do List"),
        centerTitle: true,
      ),
      body: Container(
          margin: EdgeInsets.all(20),
          child: Column(
              children: tasks
                  .map((name) => Task(
                      name: name,
                      delete: () => setState(() => tasks.remove(name))))
                  .toList() as List<Widget>)),
      floatingActionButton: IconButton(
        icon: Icon(Icons.add),
        onPressed: displayPrompt,
      ),
    );
  }
}

1 Comment

Thank you so much, I simply had to separate the MaterialApp context + return an AlertDialog widget from from the showDialog call.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.