The approach as given is correct, we just need to "inverse the truth value" of the is_list/2 test (or, more accurately, declare the proof as having failed as soon as we encounter a list). For good measure, I add a completely unnecessary message writing instruction:
is_flat([]).
is_flat([X|Xs]) :-
format("Examining ~q\n",[X]),
\+ is_list(X),
is_flat(Xs).
Then:
?- is_flat([a,b,c]).
Examining a
Examining b
Examining c
true.
But:
?- is_flat([a,[x,y,z],c]).
Examining a
Examining [x,y,z]
false.
Using maplist/2
This can be done compactly with maplist/2, which applies a predicate to each element and breaks off as soon as failure appears for such an application:
another_is_flat(List) :- maplist(not_is_list,List).
not_is_list(L) :- \+ is_list(L).
Then:
?- another_is_flat([a,b,c]).
true.
?- another_is_flat([a,[x,y,z],c]).
false.
Using forall/2
Or using CapelliC's solution. forall/2 is exactly made for this: It performs a test (in this case, \+is_list(E)) for every successful condition (in this case member(E,L)), and behaves like a short-circuiting AND (that also leaves no accidental bindings behind on success):
another_another_is_flat(L) :- forall(member(E,L),\+is_list(E)).
Then:
?- another_another_is_flat([a,b,c]).
true.
?- another_another_is_flat([a,[x,y,z],c]).
false.
Edge case:
?- another_another_is_flat([]) .
true.
The "vacuous truth": an empty list is correctly accepted.