Flutter include content into a page

Viewed 485

I actually have a searchBar(autocomplete) that is working.

When i select a result, the displaySnack() is working, it displays a snackBar, but i would like to display the content of testList().

My goal is to understand how I can launch another widget, to be able to add new widget on the page again and again.

My final goal is once i have the selected value, to make an http request, get a list as return and display a listview.

The function is executed ( i can see it in debugger ) but doesn't display anything..

(i'm new to flutter, so please explain your response if possible :) ) onSuggestionSelected : yes i know that it is void..

import 'package:drawer/src/share/snack_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import '../models/post_model.dart';
import '../services/http_service.dart';
// import 'package:http/http.dart' as http;


class PostsPage extends StatelessWidget {

  final String title;
  const PostsPage({Key? key, required this.title}) : super(key: key);
  
  static Future<List<Post>> filterList(String value) async {
    List<Post> list = await HttpService.fetchPosts();
    return list.where(
      (x) => x.title.toLowerCase().contains(value.toLowerCase())).toList();
  }  
  
  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: SafeArea(
          child: Container(
            padding: EdgeInsets.all(16),
            child: TypeAheadField<Post?>(
              debounceDuration: Duration(milliseconds: 500),
              hideSuggestionsOnKeyboardHide: false,
              textFieldConfiguration: TextFieldConfiguration(
                decoration: InputDecoration(
                  prefixIcon: Icon(Icons.search),
                  border: OutlineInputBorder(),
                  hintText: 'Select the namespace...',
                ),
              ),
              suggestionsCallback: filterList,
              itemBuilder: (context, Post? suggestion) {
                final user = suggestion!;

                return ListTile(
                  title: Text(user.title),
                );
              },
              noItemsFoundBuilder: (context) => Container(
                height: 100,
                child: Center(
                  child: Text(
                    'No Namespace Found.',
                    style: TextStyle(fontSize: 24),
                  ),
                ),
              ),
              onSuggestionSelected: (Post? suggestion) {
                final user = suggestion!;
                
                displaySnack(context, '  Namespace: '+user.title);
                testList(context);  ################################ HERE
              },
            ),
          ),
        ),
      );
      
}


  Widget testList(BuildContext context) {
    return ListView.separated(
      separatorBuilder: (BuildContext context, int index) => const Divider(),
        itemCount: 2,
        itemBuilder: (context, index) {
          return Card(
              child: ListTile(
                  title: Text("ppp"),
                  subtitle: Text("ppp"),
                  leading: CircleAvatar(
                      backgroundImage: NetworkImage(
                          "https://images.unsplash.com/photo-1547721064-da6cfb341d50"))
                  ));
        });
  }

enter image description here

I need that : https://prnt.sc/136njev

4 Answers

It is obvious that you want the widget to rebuild to show the result. The most straightforward method is to use StatefulWidget. So I use it in your case(You can also find lots of ways to manage the state List of state management approaches)

  1. Change your PostsPage to a StatefulWidget and rebuild when the user is selected
  2. Add a Column in your PostsPage and separate into 2 parts: TypeAheadField & Result
  3. Result part can use FutureBuilder (which can show loading indicator when data is not ready)

PostsPage:

class PostsPage extends StatefulWidget {
  final String title;

  const PostsPage({Key? key, required this.title}) : super(key: key);

  static Future<List<Post>> filterList(String value) async {
    // skip
  }

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

class _PostsPageState extends State<PostsPage> {
  Post? user;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SafeArea(
        child: Column(
          children: [
            Container(
              padding: EdgeInsets.all(16),
              child: TypeAheadField<Post>(
                // ... 
                // skip
                // ...
                onSuggestionSelected: (Post? suggestion) {
                  setState(() {
                    user = suggestion!;
                    displaySnack(context, '  Namespace: '+user.title);
                  });
                },
              ),
            ),
            Expanded(child: MyResult(post: user)),
          ],
        ),
      ),
    );
  }
}

Result part: (I make it an isolated StatelessWidget just for better reading. You can use the original method to build the widget)

class MyResult extends StatelessWidget {
  const MyResult({
    required this.post,
    Key? key,
  }) : super(key: key);

  final Post? post;

  Future<List<OtherObject>> getOtherObjects(Post? post) async{
    if(post == null){
      return [];
    }else{
      return Future.delayed(Duration(seconds:3),()=>[OtherObject(title: '001'),OtherObject(title: '002'),OtherObject(title: '003')]);
    }
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<OtherObject>>(
      future: getOtherObjects(post),
      builder: (context, snapshot) {
        if(snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
          final result = snapshot.data!;

          return ListView.separated(
            separatorBuilder: (BuildContext context,
                int index) => const Divider(),
            itemCount: result.length,
            itemBuilder: (context, index) {
              return Card(
                child: ListTile(
                  title: Text(result[index].title),
                  subtitle: Text("ppp"),
                  leading: CircleAvatar(
                    backgroundImage: NetworkImage(
                        "https://images.unsplash.com/photo-1547721064-da6cfb341d50"),
                  ),
                ),
              );
            },
          );
        }else {
          return Center(child: CircularProgressIndicator());
        }
      }
    );
  }
}

enter image description here

So what you are doing is basically just creating a ListView with your testList() function call and doing nothing with it, but what you want to do is to have that widget show up on the screen, right?

Flutter doesn't just show Widget if you create a new one, you must tell it to render. Just imagine you are doing preparing Widgets (e.g. Widgets in Widgets) and Flutter would render it immediately to the screen without you being finished, that wouldn't be that great.

You need to push that Widget over the Navigator widget that Flutter provides you.

onSuggestionSelected: (Post? suggestion) {
  final user = suggestion!;
            
  displaySnack(context, '  Namespace: '+user.title);
  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => testList(context)),
  );
}

I suggest you to read this article to Navigation Basics.

you can use listView builder to show the selected results.

          onSuggestionSelected: (Post? suggestion) {
            final user = suggestion!;
            
            displaySnack(context, '  Namespace: '+user.title);
            //get results
            var results = fetchResult(suggestion);
            //return a listview of the results
            return ListView.builder(
            physics: const AlwaysScrollableScrollPhysics(),
            shrinkWrap: true,
            scrollDirection: Axis.vertical,
            itemCount: results.length,
            itemBuilder: (_context, index) {
              Post post = results[index];
              return Card(
                  elevation: 2,
                  child: InkWell(
                    child: Container(
                        padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(6.0),
                          border: Border.all(color: Colors.black),
                          ),
                      child: DefaultTextStyle(
                        style: TextStyle(
                            fontWeight: FontWeight.bold,
                            fontSize: 12,
                            color: Colors.white),
                        child: Row(children: [
                          Expanded(
                            flex: 2,
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: <Widget>[
                                Text(post.data),
                              ],
                              ),
                            ),
                        ]),
                      ),
                        ),
                    onTap: () {
                      //do something when user clicks on a result
                    },
                    ));
            }
          },

If you want to show a list of selected items then you will have to add a ListView in the widget tree. Also use a StatefullWidget instead of StatelessWidget, because whenever you select an item, the selected list gets changed thus state.

sample code for state

  List<Post> selectedPosts;
  @override
  void initState() {
    super.initState();
    selectedPosts = [];
  }


  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: SafeArea(
          child: Column(
            children: [
              Container(
                padding: EdgeInsets.all(16),
                child: TypeAheadField<Post?>(
                  debounceDuration: Duration(milliseconds: 500),
                  hideSuggestionsOnKeyboardHide: false,
                  textFieldConfiguration: TextFieldConfiguration(
                    decoration: InputDecoration(
                      prefixIcon: Icon(Icons.search),
                      border: OutlineInputBorder(),
                      hintText: 'Select the namespace...',
                    ),
                  ),
                  suggestionsCallback: filterList,
                  itemBuilder: (context, Post? suggestion) {
                    final user = suggestion!;

                    return ListTile(
                      title: Text(user.title),
                    );
                  },
                  noItemsFoundBuilder: (context) => Container(
                    height: 100,
                    child: Center(
                      child: Text(
                        'No Namespace Found.',
                        style: TextStyle(fontSize: 24),
                      ),
                    ),
                  ),
                  onSuggestionSelected: (Post? suggestion) {
                    final user = suggestion!;
                    displaySnack(context, '  Namespace: '+user.title);
                  setState(()=>  selectedPosts.add(suggestion));
               
                  },
                ),
              ),
               testList(context),
            ],
          ),
        ),
      );

and in the testList function change the itemcount

itemCount: 2,

to

itemCount: selectedPosts?.length ?? 0,
Related