Flutter ListView search and click

Viewed 650

So I'm currently trying to implement some searching functionality to my ListView and this does work great actually. When I type in some letters it automatically shows me the right things (-> See Screenshot_Listview_1.png and Screenshot_Listview_2.png).

There is only one problem. I want the different texts from my listview to be clickable, so when I click on them a new ModalBottomSheet should appear. For example: I'm searching for "Apple" and when I click on the text "Apple" a ModalBottomSheet opens and I can read some facts about apples. I tried the onTap method and it works so far but I only managed to open the same BottomSheet.. But I need different BottomSheets depending on what I have tapped on.

This is what I got so far. Can you please help me out? I really don't know how to solve this problem. Thank you so much!!

import 'package:flutter/material.dart';
import 'dart:ui' as ui;


class GlossarScreen extends StatefulWidget {
  @override
  _GlossarScreenState createState() => _GlossarScreenState();
}

class _GlossarScreenState extends State<GlossarScreen> {
  TextEditingController _textEditingController = TextEditingController();

  List<String> glossarListOnSearch = [];
  List<String> glossarList = [
    'Apple',
    'Orange',
    'Banana',
    'Grapefruit',
    'Mango',
    'Kiwi',
    'Grapes',
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Glossar'),
        flexibleSpace: Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
                colors: [Color(0xffFBD23E), Color(0xffF6BE03)],
                begin: Alignment.topCenter,
                end: Alignment.bottomCenter),
          ),
        ),
        bottom: PreferredSize(
          preferredSize: Size(0, 60),
          child: Padding(
            padding: const EdgeInsets.fromLTRB(12, 0, 12, 10),
            child: Container(
              //height: 50,
              decoration: BoxDecoration(
                gradient: LinearGradient(
                    colors: [Colors.white60, Colors.white70],
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter),
                borderRadius: BorderRadius.circular(50),
              ),
              child: Padding(
                padding: const EdgeInsets.fromLTRB(20, 0, 0, 0),
                child: TextField(
                  textAlign: TextAlign.left,
                  onChanged: (value) {
                    setState(() {
                      glossarListOnSearch = glossarList
                          .where((element) => element
                              .toLowerCase()
                              .contains(value.toLowerCase()))
                          .toList();
                    });
                  },
                  controller: _textEditingController,
                  decoration: InputDecoration(
                      border: InputBorder.none,
                      errorBorder: InputBorder.none,
                      enabledBorder: InputBorder.none,
                      contentPadding: EdgeInsets.all(0),
                      hintText: 'Search'),
                ),
              ),
            ),
          ),
        ),
      ),
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
              colors: [Color(0xffFEFDFD), Color(0xffBDBDB2)],
              begin: Alignment.topLeft,
              end: Alignment.bottomRight),
        ),
        child: _textEditingController.text.isNotEmpty &&
                glossarListOnSearch.isEmpty
            ? Column(
                children: [
                  Align(
                    alignment: Alignment.center,
                    child: Padding(
                      padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
                      child: Text(
                        'No results',
                        style: TextStyle(
                            fontFamily: 'Avenir',
                            fontSize: 22,
                            color: Color(0xff848484)),
                      ),
                    ),
                  )
                ],
              )
            : ListView.builder(
                itemCount: _textEditingController.text.isNotEmpty
                    ? glossarListOnSearch.length
                    : glossarList.length,
                itemBuilder: (context, index) {
                  return GestureDetector(
                    onTap: () {
                      _testFuction(context);
                    },
                    child: Padding(
                      padding: const EdgeInsets.fromLTRB(12, 15, 12, 15),
                      child: Text(
                        _textEditingController.text.isNotEmpty
                            ? glossarListOnSearch[index]
                            : glossarList[index],
                        style: TextStyle(
                            color: Colors.black,
                            fontSize: 20,
                            fontFamily: 'Avenir'),
                      ),
                    ),
                  );
                },
              ),
      ),
    );
  }
}

void _testFuction(context) {
  showModalBottomSheet(
    context: context,
    builder: (BuildContext bc) {
      return Scaffold(
        body: Text('This text should be dependent on what I have tapped on. If I tap on "Apple" a different ModalBottomSheep shall appear then when I press on "Banana".'),
      );
    },
  );
}

Screenshot_ListView_1

Screenshot_ListView_2

3 Answers
import 'package:flutter/material.dart';
import 'dart:ui' as ui;

import 'package:stack_demo/models/FruitModel.dart';

class GlossarScreen extends StatefulWidget {
  @override
  _GlossarScreenState createState() => _GlossarScreenState();
}

class _GlossarScreenState extends State<GlossarScreen> {
  TextEditingController _textEditingController = TextEditingController();

  List<FruitModel> glossarListOnSearch = [];
  List<FruitModel> glossarList = [];

  @override
  void initState() {
    glossarList.add(FruitModel(id: 0, name: 'Apple', facts: 'Good for health'));
    glossarList.add(
        FruitModel(id: 1, name: 'Banana', facts: 'Banana is also for health'));
    glossarList.add(
        FruitModel(id: 2, name: 'Orange', facts: 'Orange good for health'));

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Glossar'),
        flexibleSpace: Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
                colors: [Color(0xffFBD23E), Color(0xffF6BE03)],
                begin: Alignment.topCenter,
                end: Alignment.bottomCenter),
          ),
        ),
        bottom: PreferredSize(
          preferredSize: Size(0, 60),
          child: Padding(
            padding: const EdgeInsets.fromLTRB(12, 0, 12, 10),
            child: Container(
              //height: 50,
              decoration: BoxDecoration(
                gradient: LinearGradient(
                    colors: [Colors.white60, Colors.white70],
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter),
                borderRadius: BorderRadius.circular(50),
              ),
              child: Padding(
                padding: const EdgeInsets.fromLTRB(20, 0, 0, 0),
                child: TextField(
                  textAlign: TextAlign.left,
                  onChanged: (value) {
                    setState(() {
                      glossarListOnSearch = glossarList
                          .where((element) => element.name!
                              .toLowerCase()
                              .contains(value.toLowerCase()))
                          .toList();
                    });
                  },
                  controller: _textEditingController,
                  decoration: InputDecoration(
                      border: InputBorder.none,
                      errorBorder: InputBorder.none,
                      enabledBorder: InputBorder.none,
                      contentPadding: EdgeInsets.all(0),
                      hintText: 'Search'),
                ),
              ),
            ),
          ),
        ),
      ),
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
              colors: [Color(0xffFEFDFD), Color(0xffBDBDB2)],
              begin: Alignment.topLeft,
              end: Alignment.bottomRight),
        ),
        child: _textEditingController.text.isNotEmpty &&
                glossarListOnSearch.isEmpty
            ? Column(
                children: [
                  Align(
                    alignment: Alignment.center,
                    child: Padding(
                      padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
                      child: Text(
                        'No results',
                        style: TextStyle(
                            fontFamily: 'Avenir',
                            fontSize: 22,
                            color: Color(0xff848484)),
                      ),
                    ),
                  )
                ],
              )
            : ListView.builder(
                itemCount: _textEditingController.text.isNotEmpty
                    ? glossarListOnSearch.length
                    : glossarList.length,
                itemBuilder: (context, index) {
                  return GestureDetector(
                    onTap: () {
                      _textEditingController.text.isNotEmpty
                          ? _testFuction(context, glossarListOnSearch[index])
                          : _testFuction(context, glossarList[index]);
                    },
                    child: Padding(
                      padding: const EdgeInsets.fromLTRB(12, 15, 12, 15),
                      child: Text(
                        _textEditingController.text.isNotEmpty
                            ? glossarListOnSearch[index].name!
                            : glossarList[index].name!,
                        style: TextStyle(
                            color: Colors.black,
                            fontSize: 20,
                            fontFamily: 'Avenir'),
                      ),
                    ),
                  );
                },
              ),
      ),
    );
  }
}

void _testFuction(context, FruitModel model) {
  showModalBottomSheet(
    context: context,
    builder: (BuildContext bc) {
      return Scaffold(
        body: Text('${model.facts}'),
      );
    },
  );
}

You can wrap the your Padding with a GestureDetector and add actions into the onTap method.

return GestureDetector(
  onTap: () {
    // TODO: Add actions onTap
  },
  child: Padding(
    padding: const EdgeInsets.fromLTRB(12, 15, 12, 15),
    child: Text(
      _textEditingController.text.isNotEmpty
      ? glossarListOnSearch[index] : glossarList[index],
      style: TextStyle(
        color: Colors.black, fontSize: 24, fontFamily: 'Avenir'),
        ),
  );
);

You need give your _testFuction some content what depends on you tap to let the bottomsheet know what it should to show.Just likes:

return GestureDetector(
  onTap:(){
    _testFuction(context,glossarListOnSearch[index]);
  }
  ...
) 

void _testFuction(context, someContent) {
  showModalBottomSheet(
    context: context,
    builder: (BuildContext bc) {
      return Scaffold(
        body: Text('This is $someContent bottomsheet'),
      );
    },
  );
}
Related