skip to Main Content

I would like to create cards based on a http response when a button is clicked, and I would like the "Search Item" and "Search Button" stay on top while the card list still can scroll, please help.

I tried to use FutureBuilder widget but it loads data when the page loads the first time (without button being pressed).

class clientRecord {
  final String englishName;
  final String chineseName;

  const clientRecord({
    required this.englishName,
    required this.chineseName,
  });

  factory clientRecord.fromJson(Map<String, dynamic> json) {
    return clientRecord(
      englishName: json['Ename'] as String,
      chineseName: json['Cname'] as String,
    );
  }
}

class homePage extends StatefulWidget {
  @override
  _homePageState createState() => _homePageState();
}

class _homePageState extends State<homePage> {
  final _searchItemController = TextEditingController();

  List<clientRecord> parseJson(String responseBody) {
    final parsed =
        convert.jsonDecode(responseBody).cast<Map<String, dynamic>>();
    return parsed
        .map<clientRecord>((json) => clientRecord.fromJson(json))
        .toList();
  }

  Future<List<clientRecord>> fetchData(http.Client client, _searchItem) async {
    final response = await client
        .get(Uri.parse('test.php'));
    return parseJson(response.body);;
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Client List')),
      body: SingleChildScrollView(
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(0, 30, 0, 20),
              child: Text(
                adminPassword(),
                style: TextStyle(
                    fontSize: 30,
                    fontWeight: FontWeight.bold,
                    color: Colors.orange[800]),
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(12.0),
              child: TextField(
                decoration: InputDecoration(
                    border: OutlineInputBorder(), labelText: 'Search Item'),
                controller: _searchItemController,
              ),
            ),
            SizedBox(height: 20),
            Container(
              height: 45,
              width: 250,
              decoration: BoxDecoration(
                  color: Colors.teal, borderRadius: BorderRadius.circular(16)),
              child: TextButton(
                onPressed: () {
                  fetchData(http.Client(), _searchItemController.text);
                },
                child: Text(
                  'Search',
                  style: TextStyle(color: Colors.white, fontSize: 20),
                ),
              ),
            ),
            FutureBuilder<List<clientRecord>>(
              future: fetchData(http.Client(), _searchItemController.text),
              builder: (context, snapshot) {
                if (snapshot.hasError) {
                  return const Center(
                    child: Text('An error has occurred!'),
                  );
                } else if (snapshot.hasData) {
                  return buildBody(dl: snapshot.data!);
                } else {
                  return const Center(
                    child: CircularProgressIndicator(),
                  );
                }
              },
            ),
          ],
        ),
      ),
    );
  }
}


class buildBody extends StatelessWidget {
  buildBody({super.key, required this.dl});

  final List<clientRecord> dl;

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
        scrollDirection: Axis.vertical,
        controller: _scrollController,
        child: Column(
          children:
          dl.map((dataRecord) => dataCard(dataRecord, context)).toList(),
        ));
  }
}
Widget dataCard(dataRecord, c) {
  return GestureDetector(
    onTap: () {
      // Navigator.push(
      //   c,
      //   MaterialPageRoute(builder: (c) => newdevelopmentdetail(dataRecord.id)),
      // );
    },
    child: Card(
      color: Colors.lightGreen[100],
      child: SizedBox(
        height: 100,
        child: Row(
          children: [
            Text(dataRecord.englishName),
          ],
        ),
      ),
    ),
  );
}



3

Answers


  1. Chosen as BEST ANSWER

    By adding a separate Future List and assign to future of FutureBuilder, setState after http call works fine.

    For scrollable cards without scroll search items, use ListView wrapped inside Expanded wrapped inside Column works perfectly.


  2. You have two separate questions here – about Future builder use; and keeping Search Item and Search Button on top. I’ll try to answer the first question.

    Your Future Builder fires because you tell it to: you call it in FutureBuilder itself:

    FutureBuilder<List<clientRecord>>(
                  future: fetchData(http.Client(), _searchItemController.text),
    

    What you can do is introduce a separate Future variable, and based on it being null or not – you create your future builder. Something like:

    Future<List<clientRecord>>? myFuture;
    
    ...
    
    child: Column(
              children: [
    ...
      if (myFuture!=null) FutureBuilder<List<clientRecord>>(
                  future: myFuture
     ...
    else Text('Press button to get list')
    
    

    And your button press should do something like (with setState being called to trigger rebuild):

    TextButton(
                    onPressed: () {
                      myFuture=fetchData(http.Client(), _searchItemController.text);
                      setState((){});
                    },
    
    Login or Signup to reply.
  3. You can make a Api call in a Future and return Column like a code snippet:

    Column(
          children: List.generate(
          // Length of List received in a response
          5,
         (index) {
         // Return a Widget as per requirement
         return Text("data");
                  },
                ),
              ),
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search