Search code examples
flutterflutter-listviewflutter-list-tile

Flutter How can I open URL that I input on textfield?


I am trying to make a bookmark app that you can input a title and URL on a dailog and list these on ListTile. I can see passed value (title and URL) on ListTile now but I cannot open URL that is passed from the dialog yet. How can I open URL that I add on ListTile?

Here is the whole code

main.dart

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Bookmark App',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home:  BookmarkListPage(text: '', url: '',),
    );
  }
}

class BookmarkListPage extends StatefulWidget {
  BookmarkListPage({super.key, title, required this.text, required this.url});

  String text;
  String url;

  final String title = 'Bookmark List';

  @override
  State<BookmarkListPage> createState() => _BookmarkListPageState();
}

class _BookmarkListPageState extends State<BookmarkListPage> {

  List<String> bookmarkList = [];
  List<String> bookmarkUrl = [];

   _launchUrl() async {
    var url = Uri.parse(bookmarkUrl as String);
    if(await canLaunchUrl(url)) {
      await launchUrl(url);
    } else {
      throw "Could not open $url";
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        title: Text(widget.title),
        bottom: PreferredSize(
          child: Container(
            height: 1,
            color: Colors.black,
          ),
          preferredSize: Size.fromHeight(1),
        ),
      ),
      body: ListView.builder(
        itemCount: bookmarkList.length,
        itemBuilder: (context, index) {
          return Card(
            child: ListTile(
              title: Text(bookmarkList[index]),
              subtitle: Text(bookmarkUrl[index]),
              // TODO if you tap the List, open the URL
              onTap: _launchUrl,
            ),
          );
        },
      ),

      // show dialog
      floatingActionButton: FloatingActionButton(
        shape: const CircleBorder(),
        backgroundColor: Colors.blue,
        onPressed: () async {
          final (newBookmarkTitle, newBookmarkUrl) = await showDialog(
              context: context,
              builder: (_) {
                return BookmarkDialog();
              });

          setState(() {
            bookmarkList.add(newBookmarkTitle);
            bookmarkUrl.add(newBookmarkUrl);
          });


        },
        child: const Icon(
          Icons.add,
          color: Colors.white,
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

dialog.dart

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

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

  @override
  BookMarkDialogState createState() => BookMarkDialogState();
}

class BookMarkDialogState extends State<BookmarkDialog> {
  String bookmarkTitle = "";
  String bookmarkUrl = "";

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('Add Bookmark'),
      content: Column(
        children: [
          Text('Title'),
          TextField(onChanged: (value) {
            setState(() {
              bookmarkTitle = value;
            });
          }),
          Text('URL'),
          TextField(
            onChanged: (value) {
              setState(() {
                bookmarkUrl = value;
              });
            },
          )
        ],
      ),
      actions: <Widget>[
        GestureDetector(
          child: const Text('Cancel'),
          onTap: () {
            Navigator.pop(context);
          },
        ),
        GestureDetector(
          child: const Text('Add'),
          onTap: () {
            Navigator.of(context).pop((bookmarkTitle, bookmarkUrl));
          },
        )
      ],
    );
  }
}

Solution

  • Update _launchUrl to accept the index of thne item from which you can get the bookmarkUrl from the list

     _launchUrl(int index) async {
        var url = Uri.parse(bookmarkUrl[index]);
        if(await canLaunchUrl(url)) {
          await launchUrl(url);
        } else {
          throw "Could not open $url";
        }
      }
    

    Then update the list item as to pass the url to _launchUrl

         Card(
            child: ListTile(
              title: Text(bookmarkList[index]),
              subtitle: Text(bookmarkUrl[index]),
              onTap:()=> _launchUrl(index),
            ),
          );